preface

Just do it.

This article describes the stages I went through during the development of these two features, and how I went from not doing MacOS reverse engineering in the first place, to learning about other people’s projects, to implementing the features I wanted. In addition, the content described in a book I was reading at that time was reflected in this development, so I wanted to record it, leave a memory of this rare experience, and light a candle for those who were confused before taking the first step. Just do it.

Plugin function based on wechatplugin-MacOS, author TK.


The main experience

Do not know how to start

TK had removed the project when it wanted to implement it, and then found that MustangYM was continuing to maintain the project. At this point, I had read two pages of Objective-C Basics and had some experience with Java and front-end development, but had no idea how to reverse MacOS or how to code OBJC. When I found MustangYM, I sent him an email. Ask if you can provide some direction, that time is July 16th.

One good thing came out of

After emailing MustangYM for some time, there was no reply. One day say to this thing with your colleagues, in the process of help him, suddenly found the TK project file have write how to compile, then according to the description of trying to run project, intermediate in pos install also because it encountered various problems, but the last is the project start up, and be able to enter the breakpoint debugging.

The function development

Once I was ready to debug, a period of time passed when I couldn’t read OBJC’s code or use Xcode, and then I put it on hold for a while. When I was developing another project for myself, I needed to connect to Evernote, but the JavaScript SDK was 401. When I failed to connect to the SDK, I found that there was a Mac native API in the development document. When I opened it, I found AppleScript script was used.

When using WeChatPlugin, I wondered why he could remotely control himself by sending messages to himself. When I read the source code, I learned that AppleScript was used to control Mac applications, and I used AppleScript to create a simple Workflow for controlling netease cloud playback and cutting songs. When I saw that Effnote supported AppleScript, I became interested. So I wrote a script to implement a feature of my project.

After completing the function, I started tinkling with AppleScript and found wechat didn’t provide a dictionary, so I tried several tests to see if I could find friends and send messages to them through specific keyboard operations. Then I tried using scripts to control wechat to send messages.

Search for friends and send the specified message
tell application "WeChat" to activate
tell application "System Events"
	key code 3 using {command down}
	keystroke "Session Name"
	delay 1
	key code 36
	key code 49 using {control down}
	delay 2
	keystroke "News"
	key code 49
	key code 36
end tell
Copy the code

Thinking of remote control, I wondered if I could also trigger a script to send a message to a designated friend.

Develop message forwarding capabilities

In the process of looking at the remote control code step by step, I found that the following method will be passed, and then I understand the autoReplyWithMsg, through debugging found that the received message is through message synchronization, and there is a replyWithMsg method in the automatic reply, it should be used to send messages. So I forgot about the remote controls and started trying to forward a message to someone in my friends when I got it.

/** hook wechat message synchronization */ - (void)hook_OnSyncBatchAddMsgs:(NSArray *)msgs isFirstSync:(BOOLArg2 {// Some other code [self autoReplyWithMsg:addMsg];
    
    if ([addMsg.fromUserName.string isEqualToString:currentUserName]
        && [addMsg.toUserName.string isEqualToString:currentUserName]) {
        [self remoteControlWithMsg:addMsg];
        [selfreplySelfWithMsg:addMsg]; }}Copy the code

Then I analyze the autoReplyWithMsg method, and at first glance I don’t even want to understand what every line is, and I think, “I don’t understand what this is, My God, help me.”

/** auto reply @param addMsg received message */ - (void)autoReplyWithMsg:(AddMsg*)addMsg { // addMsg.msgType ! =49if (! [[TKWeChatPluginConfigsharedConfig] autoReplyEnable]) return; if (addMsg.msgType ! =1&& addMsg.msgType ! =3) return;
    
    NSString *userName = addMsg.fromUserName.string;
    
    MMSessionMgr *sessionMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MMSessionMgr")];
    WCContactData*msgContact = [sessionMgr getContact:userName]; If ([msgContact isBrandContact] | | [msgContact isSelf]) {/ / the message to the public, or I send message return; }NSArray *autoReplyModels = [[TKWeChatPluginConfig sharedConfig] autoReplyModels];
    [autoReplyModels enumerateObjectsUsingBlock:^(TKAutoReplyModel *model, NSUInteger idx, BOOL* _Nonnull stop) { if (! model.enable) return; if (! model.replyContent || model.replyContent.length ==0) return;
        
        if (model.enableSpecificReply) {
            if ([model.specificContacts containsObject:userName]) {
                [self replyWithMsg:addMsg model:model];
            }
            return;
        }
        if ([addMsg.fromUserName.string containsString:@"@chatroom"] &&! model.enableGroupReply) return; if (! [addMsg.fromUserName.string containsString:@"@chatroom"] && !model.enableSingleReply) return;
        
        [self replyWithMsg:addMsg model:model];
    }];
}
Copy the code

But looking at the news of synchronization method, leaving behind a idea, that is to look not to understand a programming language, to see if it, so I went to see the last a few lines of the if and the relevant code inside, don’t know why the model is, after some time find STH over and over again is the introduction of a file, see the following content in the file, Open the auto reply Settings screen and compare, thinking, “HMM, this should be the auto reply configuration.”

#import "TKBaseModel.h"

@interface TKAutoReplyModel : TKBaseModel

@property (nonatomic, assign) BOOLenable; /**< enable auto reply */ @property (nonatomic, copy)NSString*keyword; @property (nonatomic, copy);NSString*replyContent; /**< autoreply content */ @property (nonatomic, assign)BOOLenableGroupReply; */ @property (nonatomic, assign)BOOLenableSingleReply; @property (nonatomic, assign)BOOLenableRegex; /**< enable regular matching */ @property (nonatomic, assign)BOOLenableDelay; /**< enable delay reply */ @property (nonatomic, assign)NSIntegerdelayTime; /**< delay time */ @property (nonatomic, assign)BOOLenableSpecificReply; /**< enable specific reply */ @property (nonatomic, strong)NSArray*specificContacts; /**< specific reply contact */ - (BOOL)hasEmptyKeywordOrReplyContent;

@end
Copy the code

Now that you know what it does, plus the self-description of the code, and know that this paragraph should be the relevant code to reply to a specific contact, the next step -> Add a specific contact and start testing. (in fact, I do not remember the following lines of code to write the letters 🙄, add the code to complete ️)

if (model.enableSpecificReply) {
    if ([model.specificContacts containsObject:userName]) {
        [self replyWithMsg:addMsg model:model];
    }
    return;
}
Copy the code

And then the process is going to be stripping the replyWithMsg method, Find a [[TKMessageManager shareManager] sendTextMessage: randomReplyContent toUsrName: addMsg. FromUserName. The string delay:delayTime]; This line of code is to send a message, like learn like, write a forwarding method out. How to concatenate strings? No, Google “Obejctive -c string concatenation.” How do I get a user name? No, find the same code and copy it. Complete, the method of forwarding the message comes out.

- (void)replyToMySelf:(AddMsg *)addMsg model:(TKAutoReplyModel *)model {
    NSString* msgContent;
    NSString* str1;
    MMSessionMgr *sessionMgr = [[objc_getClass("MMServiceCenter") defaultCenter] getService:objc_getClass("MMSessionMgr")];
    WCContactData*msgContact = [sessionMgr getContact:addMsg.fromUserName.string]; Str1 = [msgContact.m_nsnickName stringByAppendingString:@"\n---\n"];
    msgContent = [str1 stringByAppendingString:addMsg.content.string];
    NSInteger delayTime = 0; // The message will be formatted to the wechat signal // my local code, write a dead wechat account, here use username instead of [[TKMessageManager shareManager] sendTextMessage:msgContent toUsrName:@"username"delay:delayTime]; // Send the user's wechat id to username.TKMessageManager shareManager] sendTextMessage:addMsg.fromUserName.string toUsrName:@"username" delay:delayTime];
}
Copy the code

Develop the ability to send messages to designated friends

After all this, the WeChatPlugin code began to look familiar. In the message synchronization method above, add the following code, OK, done.

if ([addMsg.fromUserName.string isEqualToString:@"username"]
    && [addMsg.toUserName.string isEqualToString:currentUserName]) {
    NSString *content = addMsg.content.string;
    if ([content hasPrefix:@"Reply:"]) {
        NSArray *list = [content componentsSeparatedByString: @":"];
        if ([list count] == 3) {
            NSString *toUserName = list[1];
            NSString *toContent = list[2];
            [[TKMessageManager shareManager] sendTextMessage:toContent toUsrName:toUserName delay:0];
        }
    } else {
        [selfremoteControlWithMsg:addMsg]; }}Copy the code

conclusion

In the process of realizing these two functions, I felt how each point in life influenced each other, and realized how the contents in the book were verified by myself. I also gained the joy of realizing my own ideas with what I learned. Thanks to TK’s encapsulation, I can implement the functionality I want so easily. The code is not perfect, but it can meet my current needs, and after this development, I have made another step forward in my own software, and the imperfect code can be constantly improved in the future.

Trying too hard to be perfect can stop you from moving forward. Not striving for perfection will stop you from growing. ‘.