Android--抢红包神器 辅助功能Accessibility 最新适配

新鲜出炉的抢红包神器

前提:实现一个微信自动抢红包并不是很难,原理就是利用android的辅助功能,监听一下窗口变化,找到对应控件ID,模拟点击。写好一个service服务类即可完成。但上手会发现这其中还是有很多问题的,所以我们主要是适配版本解决问题。

一、问题

关于抢红包神器的文章也很多,但使用起来效果却不佳,总结一下会有以下几个问题 (看看是不是就是你们遇到的问题):

  • 1. 红包来源

大多数抢红包神器只适配了从通知栏进入的红包 也就是微信在后台 此时来了红包,监听通知栏,然后点进去【抢】。但是事实上我认为还需要适配两个场景,一个是在聊天列表页来红包,一个是在好友会话页面来了红包。

  • 2. 微信版本更新,抢红包神器红能就不能用了

这个就是需要各个版本进行适配 因为微信每更新版本,每个控件的id会发生变化,类名有时候甚至都会混淆,改变。所以当类名、id名称发生改变时,程序找id为null,也就没有办法继续操作点击,从而导致抢红包神器功能失效。

  • 3. 卡在【拆】的页面不继续

关于这个问题有以下发生的可能,第一种就是【拆】这个button的id发生了变化,第二种是手机版本大于21,在抢红包这个页面可能存在window嵌套,不能单单在getRootInActiveWindow 查找,需要对21前和21后的版本进行适配。亲测有效。

二、解决方案

  • 1. 红包来源问题

若想实现在聊天列表页和好友会话页面的红包监听,需要我们监听窗口内容变化,也就是AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED,当监听到窗口内容变化时做相应的处理。

  • 2. 适配微信版本

本地适配,存放在一个静态 map中 key存放微信版本 value存放一个需要适配的id合集对象。然后在服务开始前判断当前手机安装微信版本,然后去本地map中取出相应的id合集。这样使用map存储版本号以及需要适配的id值来做兼容。或是直接适配最新的版本,用户版本过低,提示升级。

  • 3.拆红包

手机版本21前后做适配

三、了解android辅助功能Accessibility

AccessibilityServiceInfo
AccessibilityService
AccessibilityManager
AccessibilityEvent
Build accessibility services

AccessibilityService

AccessibilityServiceInfo

AccessibiliyEvent

AccessibilityManager

四、需要适配的对象(id名称、class名称等)

我们把需要适配的对象起名为WxChatTag 云控更新(构建json)或本地静态map存储。

public static class WxChatTag implements Serializable {
        //4个Activity名字
        private String ChatListClassName;//聊天列表页
        private String ConversationClassName;//会话页面
        private String PopOpenRedPacketClassName;//弹出开红包的页面
        private String MoneyDetailClassName;//红包领取详情页面
        //id
        private String OpenRedPacketButtonId;//拆红包的id
        private String MoneyDetailTextId;//红包详情页的钱数id
        //会话详情页
        private String ConversationPageListViewId;//会话页面的listview的id
        private String ConversationPageRedPacketItemId;//会话页面红包View的id
        private String ConversationPageRecentMsgId;//会话页面最新消息的id

        //聊天记录列表页面
        private String ChatListPageItemId;//聊天列表页面Item的id
        private String ChatListPageRedHintId;//聊天列表页红色圆点提示view的id
        private String ChatListPageKeyTextId;//聊天列表页新消息信息text的id

        private String KeySearchName;//会话列表页面查找红包的关键词 领取红包
//getter setter
}

关于class name名字获取的技巧 【adb命令】

adb shell dumpsys activity | grep "mFocusedActivity" 打印栈顶当前activity

关于id对应情况如下图示

MoneyDetailTextId

ConversationPageRedPacketItemId

ConversationPageListViewId

ConversationPageRecentMsgId

OpenRedPacketButtonId

ChatListPageItemId;

ChatListPageKeyTextId

ChatListPageRedHintId

五、简易流程图

以下为我的抢红包神器的主要流程图 大家作为参考。

为了适配微信版本,首先要判断当前用户的微信版本和你适配的微信版本是否相同。然后进行自定义操作。你可以本地map存储取值,也可以动态适配,网络请求。

监听onAccessibilityEvent

监听通知栏

监听到通知栏的后续操作

监听到窗口变化
首先判断当前activity 一共有四个activity窗口需要我们做处理,其他不做处理。

监听窗口变化

监听内容变化
这一部的主要目的就是实现在聊天列表页和好友会话页面的红包监听。

监听内容变化

六、主要代码

public class MyWxQhbService extends AccessibilityService {

    /**
     * 微信的包名
     */
    public static final String PACKAGE_NAME = "com.tencent.mm";

    /**
     * 红包消息的关键字
     */
    private static final String KEY = "[微信红包]";

    private static final int APP_STATE_BACKGROUND = -1;
    private static final int APP_STATE_FOREGROUND = 1;

    private static final int WINDOW_LAUNCHER_UI = 1; // ChatListClassName
    private static final int WINDOW_LUCKY_MONEY_OPEN = 2; // PopOpenRedPacketClassName
    private static final int WINDOW_LUCKY_MONEY_DETAILUI = 3; // MoneyDetailClassName
    private static final int WINDOW_OTHER = -1;

    private static final int SILENCE_TIME = 1300;

    private int mAppState = APP_STATE_FOREGROUND;
    private int mCurrentWindow = WINDOW_OTHER;
    private boolean isSilence; // 沉默, 防止没抢到红包而反复点

    private static String ChatListClassName = "com.tencent.mm.ui.LauncherUI"; // 聊天列表页
    private static String ConversationClassName = "com.tencent.mm.ui.LauncherUI"; //会话页面
    private static String PopOpenRedPacketClassName = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyReceiveUI"; //弹出开红包的页面
    private static String MoneyDetailClassName = "com.tencent.mm.plugin.luckymoney.ui.LuckyMoneyDetailUI"; // 红包领取详情页面


    private static String OpenRedPacketButtonId = "com.tencent.mm:id/cnu"; //  拆红包页面 '开'的id
    private static String MoneyDetailTextId = "com.tencent.mm:id/cnu";//红包详情页 钱数id


    private static String ChatListPageItemId = "com.tencent.mm:id/azj"; // 聊天列表页面Item的id
    private static String ChatListPageRedHintId = "com.tencent.mm:id/lu"; // 聊天列表页红色圆点提示view的id
    private static String ChatListPageKeyTextId = "com.tencent.mm:id/azn"; // 聊天记录列表中Item关键词的id

    private static String ConversationPageListViewId = "com.tencent.mm:id/ahf"; // 会话页面的listview的id
    private static String ConversationPageRedPacketItemId = "com.tencent.mm:id/aku"; // 会话页面红包View的id
    private static String ConversationPageRecentMsgId = "com.tencent.mm:id/a5"; // 会话页面最新一条信息的id

    private static String KeySearchName = "领取红包";//会话页面关键字搜索


    private PackageInfo mWeChatPackageInfo;
    private Intent intent = new Intent("com.wx.qhb.receiver");


    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();
        Log.d("qhb", "onServiceConnected");

        updateConfig();
    }

    /**
     * 更新可变id、class的Config
     */
    private void updateConfig() {
        Log.d("qhb", "updateConfig");
        //获取微信包信息
        mWeChatPackageInfo = PackageUtils.getPackageInfo(getApplicationContext(), "com.tencent.mm");
        final int versionCode = mWeChatPackageInfo.versionCode;
        Log.d("qhb", "微信:versionCode:" + versionCode);
        if (versionCode == 1360) {
            return;
        } else {
            Log.d("qhb", "versionCode>1360");
            //网络请求
            ConfigHelper.getConfig(getApplicationContext(), new ConfigHelper.RequestConfigCallBack() {
                @Override
                public void onRequestFailed() {
                    Toast.makeText(getApplicationContext(), "网络错误,启动工具失败", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onSuccess(ConfigBean.Config config) {
                    if (config != null) {
                        boolean isInit = false;
                        List<ConfigBean.WeChatVersionConfig> weChatVersionConfigs = config.getWeChatVersionConfigs();
                        for (ConfigBean.WeChatVersionConfig weChatVersionConfig : weChatVersionConfigs) {
                            if (weChatVersionConfig.getWeChatVersionCode() == versionCode) {
                                isInit = true;
                                initField(weChatVersionConfig.getWxChatTag());
                            }
                        }
                        if (!isInit) {
                            //说明没有适配该微信版本
                            Log.d("qhb", "发送广播");
                            sendBroadcast(intent);
                        }

                    }

                }
            });
        }


    }

    /**
     * 赋值config
     *
     * @param mWxChatTag
     */
    private void initField(ConfigBean.WxChatTag mWxChatTag) {
        Log.d("qhb", "initField");
        if (mWxChatTag == null) {
            return;
        }
        ChatListClassName = mWxChatTag.getChatListClassName();
        ConversationClassName = mWxChatTag.getConversationClassName();
        PopOpenRedPacketClassName = mWxChatTag.getPopOpenRedPacketClassName();
        MoneyDetailClassName = mWxChatTag.getMoneyDetailClassName();


        OpenRedPacketButtonId = mWxChatTag.getOpenRedPacketButtonId();
        MoneyDetailTextId = mWxChatTag.getMoneyDetailTextId();


        ChatListPageItemId = mWxChatTag.getChatListPageItemId();
        ChatListPageRedHintId = mWxChatTag.getChatListPageRedHintId();
        ChatListPageKeyTextId = mWxChatTag.getChatListPageKeyTextId();

        ConversationPageListViewId = mWxChatTag.getConversationPageListViewId();
        ConversationPageRecentMsgId = mWxChatTag.getConversationPageRecentMsgId();
        ConversationPageRedPacketItemId = mWxChatTag.getConversationPageRedPacketItemId();

        KeySearchName = mWxChatTag.getKeySearchName();
    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        switch (event.getEventType()) {
            case AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED: // 通知栏状态变化
                notificationEvent(event);
                break;
            case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: // 窗口状态变化
                windowStateEvent(event);
                break;
            case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED:  // 窗口内容变化
                windowContentEvent(event);
                break;
            default:
                break;
        }
    }

    /**
     * 微信是否运行在前台
     */
    private boolean isRunningForeground(Context context) {
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
        if (!tasks.isEmpty()) {
            String packageName = tasks.get(0).topActivity.getPackageName();
            if (PACKAGE_NAME.equals(packageName)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 处理通知栏事件
     */
    private void notificationEvent(AccessibilityEvent event) {
        Log.d("qhb", "通知栏事件");
        if (!isRunningForeground(getApplicationContext())) {
            mAppState = APP_STATE_BACKGROUND;
        } else {
            mAppState = APP_STATE_FOREGROUND;
        }

        Parcelable data = event.getParcelableData();
        if (data == null || !(data instanceof Notification)) {
            return;
        }

        List<CharSequence> texts = event.getText();
        if (!texts.isEmpty()) {
            String text = String.valueOf(texts.get(0));
            int index = text.lastIndexOf(":");
            if (index != -1) {
                text = text.substring(index + 1);
            }
            if (text.contains(KEY)) {
//                isHasRedPacket = true;
                Notification nf = (Notification) data;
                PendingIntent pendingIntent = nf.contentIntent;
                if (NotifyUtils.isLockScreen(getApplicationContext())) {  // 是否为锁屏或黑屏状态
                    if (SpUtils.isLockScreenAutomaticGrab()) {
                        NotifyUtils.wakeAndUnlock(getApplicationContext());
                        NotifyUtils.send(pendingIntent); // 打开微信
                    } else {
                        NotifyUtils.showNotify(getApplicationContext(), String.valueOf(nf.tickerText), pendingIntent); // 显示有红包通知
                    }
                } else {
                    NotifyUtils.send(pendingIntent); // 打开微信
                }

                // 播放声音和震动
                NotifyUtils.playEffect(getApplicationContext());
            }
        }
    }

    /**
     * 窗口状态变化
     */
    private void windowStateEvent(AccessibilityEvent event) {
//        Log.d("qhb", "窗口状态变化");
        CharSequence className = event.getClassName();
        if (className == null) {
            return;
        }
        String name = className.toString();

        if (className.equals(ChatListClassName)) {
            mCurrentWindow = WINDOW_LAUNCHER_UI;
        } else if (className.equals(ConversationClassName)) {
            mCurrentWindow = WINDOW_LAUNCHER_UI;
        } else if (className.equals(PopOpenRedPacketClassName)) {
            mCurrentWindow = WINDOW_LUCKY_MONEY_OPEN;
        } else if (className.equals(MoneyDetailClassName)) {
            mCurrentWindow = WINDOW_LUCKY_MONEY_DETAILUI;
        } else {
            mCurrentWindow = WINDOW_OTHER;

        }

        switch (mCurrentWindow) {
            case WINDOW_LAUNCHER_UI:
                clickRedPackets(); // 在聊天界面, 去点中红包
                break;
            case WINDOW_LUCKY_MONEY_OPEN:
                getLuckyMoney();
                break;
            case WINDOW_LUCKY_MONEY_DETAILUI:
                detailsRedPacket(); //看详细的纪录界面
                break;
        }


    }

    /**
     * 窗口内容变化
     */
    private void windowContentEvent(AccessibilityEvent event) {
        Log.d("qhb", "窗口内容变化");
        if (mCurrentWindow != WINDOW_LAUNCHER_UI) { // //不在聊天界面或聊天列表,不处理
            return;
        }

        AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
        if (nodeInfo == null) {
            return;
        }

        // 直接去获取当前会话的最后一条Item, 不为null, 则是当前会话列表
        AccessibilityNodeInfo item = AccessibilityUtils.findNodeInfosByIdLast(nodeInfo, ConversationPageRecentMsgId);
        if (item != null) {
            if (isSilence) { // 沉默中, return
                return;
            }
            clickLastMsg(nodeInfo);
            return;
        }

        // 直接去获取聊天记录的第一条Item, 不为null, 则是聊天记录列表
        item = AccessibilityUtils.findNodeInfosById(nodeInfo, ChatListPageItemId); //第一条消息
        if (item != null) {
            AccessibilityNodeInfo red = AccessibilityUtils.findNodeInfosById(item, ChatListPageRedHintId);
            if (red != null) { // 有小圆点, 说明有未读消息
                AccessibilityNodeInfo label = AccessibilityUtils.findNodeInfosById(item, ChatListPageKeyTextId);
                if (label != null) {
                    String text = String.valueOf(label.getText());
                    Log.d("qhb", "列表页" + label.getText());
                    int index = text.lastIndexOf(":");
                    if (index != -1) {
                        text = text.substring(index + 1);
                    }
                    if (text.contains(KEY)) {
//                        isHasRedPacket = true;
                        // 有红包, 点开item
                        AccessibilityUtils.performClick(label);
                    }
                }
            }
            return;
        }
    }

    private void clickRedPackets() {

        AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();
        if (nodeInfo == null) {
            return;
        }
        //分两种情况1.在聊天列表页
        //2.在聊天会话页
        //这两个页面的名称都是com.tencent.mm.ui.LauncherUI这个名字
        AccessibilityNodeInfo listNode = AccessibilityUtils.findNodeInfosByTexts(nodeInfo, KEY);
        if (listNode != null) {
            Log.d("qhb", "聊天列表的微信红包的node" + "不为空");
            AccessibilityUtils.performClick(listNode.getParent().getParent().getParent().getParent());
            return;
        } else {
            Log.d("qhb", "聊天列表的微信红包的node" + "为空");
        }
        // 聊天会话窗口,遍历节点匹配
        AccessibilityNodeInfo node = AccessibilityUtils.findNodeInfosByText(nodeInfo, KeySearchName);
        if (node != null) {
            Log.d("qhb", "会话页领取红包的node" + "不为空");
            AccessibilityUtils.performClick(node);
        } else {
            Log.d("qhb", "会话页领取红包的node" + "为空");
            return;
        }

    }


    /**
     * 点最新消息
     */
    private void clickLastMsg(AccessibilityNodeInfo nodeInfo) {
        AccessibilityNodeInfo listView = AccessibilityUtils.findNodeInfosById(nodeInfo, ConversationPageListViewId); //找到聊天会话页面的会话内容的listview
        if (listView == null) {
            return;
        }

        if (mWeChatPackageInfo.versionCode >= 1360 && listView.getChildCount() > 0) {
            listView = listView.getChild(0);
        }

        int childCount = listView.getChildCount();
        if (childCount <= 0) {
            return;
        }
        AccessibilityNodeInfo item = listView.getChild(childCount - 1);
        if (item != null) { // 每一条新消息都试着点红包
            AccessibilityNodeInfo real = AccessibilityUtils.findNodeInfosById(item, ConversationPageRedPacketItemId);
            if (real != null) { // 真红包

                // 新版本后, 1100(包括)以上, 能判断红包是否已经领取
                if (mWeChatPackageInfo.versionCode >= 1100) {
                    AccessibilityNodeInfo realToo = AccessibilityUtils.findNodeInfosByText(real, KeySearchName);
                    if (realToo == null) {
                        return;
                    }
                }
                AccessibilityUtils.performClick(real);
            }

        }
        return;
    }

    /**
     * 抢红包
     */
    private void getLuckyMoney() {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            List<AccessibilityWindowInfo> nodeInfos = getWindows();
            for (AccessibilityWindowInfo window : nodeInfos) {
                AccessibilityNodeInfo nodeInfo = window.getRoot();
                if (nodeInfo == null) {
                    break;
                }
                List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId(OpenRedPacketButtonId);
                if (list != null && list.size() > 0) {
                    AccessibilityUtils.performOpenRedPacketWithDelay(list.get(0));
                    return;
                }
            }
        }

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {
            AccessibilityNodeInfo nodeInfo = getRootInActiveWindow();  //获得整个窗口对象
            if (nodeInfo == null) {
                return;
            }

            List<AccessibilityNodeInfo> list = nodeInfo.findAccessibilityNodeInfosByViewId(OpenRedPacketButtonId);
            if (list != null && list.size() > 0) {
                AccessibilityUtils.performOpenRedPacketWithDelay(list.get(0));
                return;
            }

            //如果没找到拆红包的button,则将界面上所有子节点都点击一次
            for (int i = nodeInfo.getChildCount() - 1; i >= 0; i--) {
                if (("android.widget.Button").equals(nodeInfo.getChild(i).getClassName())) {
                    AccessibilityUtils.performOpenRedPacketWithDelay(nodeInfo.getChild(i));
                    return;
                }
            }
//            Toast.makeText(this, "未找到开红包按钮", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 领取详情
     */
    private void detailsRedPacket() {
        // 到这, 领取流程算是完了
        Log.d("qhb", "领取流程完事");

        List<AccessibilityNodeInfo> moneyNode = getRootInActiveWindow().findAccessibilityNodeInfosByViewId(MoneyDetailTextId);
        if (moneyNode != null && moneyNode.size() > 0) {
            String moneyStr = moneyNode.get(0).getText().toString();
            float money = Float.parseFloat(moneyStr);
            SpUtils.put("totalNum", SpUtils.get("totalNum", 0) + 1);
            SpUtils.put("totalMoney", SpUtils.get("totalMoney", 0f) + money);
        }
        back();

    }

    /**
     * 返回
     */
    private void back() {
        back(-1);
    }

    /**
     * 返回
     */
    private void back(int count) {
        Log.d("qhb", "back:" + count);
        silence(); // 沉默

        int backCount;
        if (mAppState == APP_STATE_BACKGROUND) {
            mAppState = APP_STATE_FOREGROUND;
            backCount = 3;
        } else {
            backCount = 1;
        }

        if (count != -1) {
            backCount = count;
        }

        for (int i = 0; i < backCount; i++) {
            AccessibilityUtils.performBack(this);
            if (i < backCount - 1) {
                SystemClock.sleep(666);// 需要个时间差
            }
        }
    }

    /**
     * 沉默
     */
    private void silence() {
        isSilence = true; // 开启沉默
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                isSilence = false;
            }
        }, SILENCE_TIME);
    }


    @Override
    public void onInterrupt() {
        Log.d("qhb", "onInterrupt 抢红包服务中断");
    }

}

七、效果图

开启.gif
通知栏的红包.gif
聊天列表页来了红包.gif

会话页的红包.gif

八、apk体验链接

apk体验链接

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,881评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,052评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,598评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,407评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,823评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,872评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,037评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,778评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,505评论 1 247
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,745评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,233评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,231评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,141评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,939评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,954评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,784评论 2 275

推荐阅读更多精彩内容

  • 生命其实很短,只有两天 一天用来出生,而一天用来死亡 它们相遇的一刻,也就有了生死存亡 只是生命一开始,就有了区别...
    周延龙阅读 247评论 0 1
  • 01 孔子曰:吾十有五而志于学。15岁,这个年龄节点很有意思,按照当前的教育阶段,是一个孩子从初中毕业进入高中之际...
    04b12f4ce9fd阅读 565评论 0 2