├── .gitignore ├── Info.plist ├── Makefile ├── README.md ├── ShortLook-API.h ├── WeChatContactPhotoProvider.h ├── WeChatContactPhotoProvider.m └── control /.gitignore: -------------------------------------------------------------------------------- 1 | # gitignore for Theos 2 | 3 | *.deb 4 | _/ 5 | theos/ 6 | theos 7 | .theos 8 | packages/ 9 | ./packages/ 10 | obj/ 11 | ./obj/ 12 | obj 13 | .DS_Store 14 | -------------------------------------------------------------------------------- /Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDisplayName 6 | WeChat 7 | DDNotificationExternalProviderClasses 8 | 9 | WeChatContactPhotoProvider 10 | com.tencent.xin 11 | 12 | DDNotificationExternalProviderAPIVersion 13 | 1 14 | 15 | 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | PACKAGE_VERSION = 1.0.2 3 | DEBUG = 0 4 | ARCHS = armv7 arm64 5 | 6 | include $(THEOS)/makefiles/common.mk 7 | 8 | BUNDLE_NAME = ShortLook-WeChat 9 | $(BUNDLE_NAME)_CFLAGS = -fobjc-arc 10 | $(BUNDLE_NAME)_FILES = $(wildcard *.m) 11 | $(BUNDLE_NAME)_FRAMEWORKS = UIKit MobileCoreServices 12 | $(BUNDLE_NAME)_INSTALL_PATH = /Library/Dynastic/ShortLook/Plugins/ContactPhotoProviders 13 | 14 | include $(THEOS_MAKE_PATH)/bundle.mk 15 | 16 | BUNDLE_PATH = $($(BUNDLE_NAME)_INSTALL_PATH)/$(BUNDLE_NAME).bundle 17 | 18 | internal-stage:: 19 | $(ECHO_NOTHING)mkdir -p $(THEOS_STAGING_DIR)$(BUNDLE_PATH)$(ECHO_END) 20 | $(ECHO_NOTHING)cp Info.plist $(THEOS_STAGING_DIR)$(BUNDLE_PATH)/Info.plist$(ECHO_END) 21 | 22 | after-install:: 23 | install.exec "killall -9 SpringBoard" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ShortLook-WeChat 2 | -------------------------------------------------------------------------------- /ShortLook-API.h: -------------------------------------------------------------------------------- 1 | // 2 | // WARNING: 3 | // This file contains the required headers to compile a tweak that 4 | // interacts with ShortLook's contact photo API. Do not modify the 5 | // headers within, or it could lead to unexpected behaviour. 6 | // 7 | // --- 8 | // 9 | // ShortLook-API.h 10 | // 11 | // Created by AppleBetas on 2018-05-18. 12 | // Copyright © 2018 Dynastic Development. All rights reserved. 13 | // 14 | 15 | #import 16 | @class NCNotificationRequest, UNNotificationContent; 17 | 18 | /// A ShortLook-displayable notification representing a real user notification sent by an application to the system. 19 | @interface DDUserNotification : NSObject 20 | /// A custom notification title, separate from the application's title. 21 | @property (nonatomic, retain, readonly) NSString *notificationTitle; 22 | 23 | /// A dictionary of any extra information included by ShortLook. 24 | @property (nonatomic, retain) NSDictionary *userInfo; 25 | 26 | /// The system notification request that created this notification. 27 | @property (nonatomic, readonly, retain) NCNotificationRequest *request; 28 | 29 | /// The user notification's content, sent by the application. 30 | - (UNNotificationContent *)content; 31 | 32 | /// A dictionary of any extra information provided by the application sending the notification. 33 | - (NSDictionary *)applicationUserInfo; 34 | 35 | /// The bundle identifier of the application this notification represents. 36 | - (NSString *)senderIdentifier; 37 | @end 38 | 39 | /// An object representing settings for the photo to be provided by a promise. 40 | @interface DDNotificationContactPhotoSettings: NSObject 41 | /// The background colour to show for the contact photo view if the provided image contains any transparency. 42 | @property (nonatomic, retain) UIColor *backgroundColor; 43 | 44 | /// Whether or not ShortLook should automatically cache the returned image from your provider and use it for all future notifications with the same photo identifier and application. 45 | @property (nonatomic, assign) BOOL usesCaching; 46 | @end 47 | 48 | /// A promise representing a commitment to providing a contact icon for a notification. 49 | @interface DDNotificationContactPhotoPromise: NSObject 50 | /// An object holding the settings pertaining to the photo to be displayed. 51 | @property (nonatomic, retain) DDNotificationContactPhotoSettings *settings; 52 | 53 | /// Whether or not this promise has already been resolved or rejected. 54 | @property (nonatomic, readonly, assign) BOOL isComplete; 55 | 56 | // MARK: - Provider methods 57 | 58 | /// Resolve this promise with the provided image, notifying ShortLook that you have received your image. 59 | /// - NOTE: This method should only be ran from within `addResolver:`. 60 | - (void)resolveWithImage:(UIImage *)image; 61 | 62 | /// Reject this promise, notifying ShortLook that you failed to receive an image. 63 | /// - NOTE: This method should only be ran from within `addResolver:`. 64 | - (void)reject; 65 | @end 66 | 67 | /// An offer to fulfill a promise representing a commitment to providing a contact icon for a notification. 68 | @interface DDNotificationContactPhotoPromiseOffer: NSObject 69 | /// A unique identifier for the photo that will be provided by this promise. 70 | @property (nonatomic, readonly, retain) NSString *photoIdentifier; 71 | 72 | /// A string to replace the notification's title with, if it is required for your provider's context. Set to "" to remove the provided notification's title. 73 | @property (nonatomic, retain) NSString *titleOverride; 74 | 75 | /// A string to replace the notification's subtitle with, if it is required for your provider's context. Set to "" to remove the provided notification's subtitle. 76 | @property (nonatomic, retain) NSString *subtitleOverride; 77 | 78 | /// A string to replace the notification's body with, if it is required for your provider's context. Set to "" to remove the provided notification's body. 79 | @property (nonatomic, retain) NSString *bodyOverride; 80 | 81 | /// Initialize a promise with the provided photo identifier. 82 | - (instancetype)initWithPhotoIdentifier:(NSString *)photoIdentifier; 83 | 84 | /// Create a promise offer that will return the image at the provided URL, if needed. 85 | + (instancetype)offerDownloadingPromiseWithPhotoIdentifier:(NSString *)photoIdentifier fromURL:(NSURL *)url; 86 | 87 | /// Create a promise offer that will return the image at the provided URL, if needed, with custom settings. 88 | + (instancetype)offerDownloadingPromiseWithPhotoIdentifier:(NSString *)photoIdentifier fromURL:(NSURL *)url withSettings:(DDNotificationContactPhotoSettings *)settings; 89 | 90 | /// Create a promise offer that will instantly return the provided image. 91 | + (instancetype)offerInstantlyResolvingPromiseWithPhotoIdentifier:(NSString *)photoIdentifier image:(UIImage *)image; 92 | 93 | /// Create a promise offer that will instantly return the provided image with custom settings. 94 | + (instancetype)offerInstantlyResolvingPromiseWithPhotoIdentifier:(NSString *)photoIdentifier image:(UIImage *)image withSettings:(DDNotificationContactPhotoSettings *)settings; 95 | 96 | /// Add the block that will run if your image is needed (as it will not be in some cases, such as if your image is already cached by ShortLook). 97 | /// If your provider does any long-running or asynchronous operations, they should be done using this method. 98 | /// Any code run inside the provider block will be performed on a background thread. 99 | - (void)fulfillWithBlock:(void (^)(DDNotificationContactPhotoPromise *promise))block; 100 | @end 101 | 102 | /// An object that can provide contact photos for ShortLook notifications. 103 | @protocol DDNotificationContactPhotoProviding 104 | @required 105 | /// Returns an offer to fulfill a promise to provide a contact photo for a notification. 106 | - (DDNotificationContactPhotoPromiseOffer *)contactPhotoPromiseOfferForNotification:(DDUserNotification *)notification; 107 | @end 108 | -------------------------------------------------------------------------------- /WeChatContactPhotoProvider.h: -------------------------------------------------------------------------------- 1 | #import "ShortLook-API.h" 2 | #import 3 | 4 | @interface WeChatContactPhotoProvider : NSObject { 5 | sqlite3 *db; 6 | } 7 | - (DDNotificationContactPhotoPromiseOffer *)contactPhotoPromiseOfferForNotification:(DDUserNotification *)notification; 8 | @end 9 | -------------------------------------------------------------------------------- /WeChatContactPhotoProvider.m: -------------------------------------------------------------------------------- 1 | #import "WeChatContactPhotoProvider.h" 2 | #import 3 | 4 | @interface FBApplicationInfo 5 | -(NSURL *)dataContainerURL; 6 | @end 7 | 8 | @interface LSApplicationProxy 9 | +(id)applicationProxyForIdentifier:(id)arg1; 10 | @end 11 | 12 | @implementation WeChatContactPhotoProvider 13 | 14 | - (DDNotificationContactPhotoPromiseOffer *)contactPhotoPromiseOfferForNotification:(DDUserNotification *)notification { 15 | FBApplicationInfo *appinfo = [LSApplicationProxy applicationProxyForIdentifier:@"com.tencent.xin"]; 16 | NSString *appPath = [[appinfo dataContainerURL] path]; 17 | NSString *contactID = notification.applicationUserInfo[@"u"]; 18 | if (!contactID) return nil; 19 | 20 | NSString *loginInfoPath = [NSString stringWithFormat:@"%@/Documents/LocalInfo.lst", appPath]; 21 | NSDictionary *loginInfo = [NSDictionary dictionaryWithContentsOfFile:loginInfoPath]; 22 | NSString *userID = loginInfo[@"$objects"][2]; 23 | NSString *userDataName = [self md5:userID]; 24 | NSString *userContactsDBPath = [NSString stringWithFormat:@"%@/Documents/%@/DB/WCDB_Contact.sqlite", appPath, userDataName]; 25 | 26 | if (sqlite3_open([userContactsDBPath UTF8String], &db) != SQLITE_OK) { 27 | sqlite3_close(db); 28 | return nil; 29 | } 30 | 31 | NSString *profileURLStr; 32 | 33 | NSString *sqlQuery = [NSString stringWithFormat:@"SELECT dbContactHeadImage FROM Friend WHERE userName = '%@'", contactID]; 34 | sqlite3_stmt * statement; 35 | 36 | if (sqlite3_prepare_v2(db, [sqlQuery UTF8String], -1, &statement, nil) == SQLITE_OK) { 37 | while (sqlite3_step(statement) == SQLITE_ROW) { 38 | const void *head = sqlite3_column_blob(statement, 0); 39 | int size = sqlite3_column_bytes(statement, 0); 40 | NSData *data = [[NSData alloc] initWithBytes:head length:size]; 41 | profileURLStr = [self getAvatarURLInData:data]; 42 | } 43 | } 44 | sqlite3_close(db); 45 | 46 | NSRange range = [profileURLStr rangeOfString:@"/132" options:NSBackwardsSearch]; 47 | if (range.location != NSNotFound) { 48 | //替换为高清头像 49 | profileURLStr = [profileURLStr stringByReplacingCharactersInRange:range withString:@"/0"]; 50 | } 51 | 52 | if (!profileURLStr) return nil; 53 | NSURL *profileURL = [NSURL URLWithString:profileURLStr]; 54 | if (!profileURL) return nil; 55 | 56 | return [NSClassFromString(@"DDNotificationContactPhotoPromiseOffer") offerDownloadingPromiseWithPhotoIdentifier:profileURLStr fromURL:profileURL]; 57 | } 58 | 59 | - (NSString *)getAvatarURLInData:(NSData *)data{ 60 | if (!data || data.length <= 8) { 61 | return @""; 62 | } 63 | 64 | int begin = 0; 65 | int end = 0; 66 | 67 | Byte *byteData = (Byte *)[data bytes]; 68 | for(int i=0;i<[data length];i++){ 69 | if (byteData[i] == 104 && begin == 0) { 70 | begin = i; 71 | } 72 | 73 | if (byteData[i] == 26 && end == 0) { 74 | end = i; 75 | } 76 | } 77 | 78 | if (begin > 0 && end > 0) { 79 | int len = end - begin; 80 | NSData* tempData = [data subdataWithRange:NSMakeRange(begin, len)]; 81 | NSString* str = [[NSString alloc]initWithData:tempData encoding:NSASCIIStringEncoding]; 82 | 83 | return str; 84 | } 85 | 86 | return @""; 87 | } 88 | 89 | - (NSString *)md5:(NSString *)input { 90 | 91 | const char *cStr = [input UTF8String]; 92 | 93 | unsigned char digest[CC_MD5_DIGEST_LENGTH]; 94 | 95 | CC_MD5( cStr, strlen(cStr), digest ); // This is the md5 call 96 | 97 | NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2]; 98 | 99 | for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) { 100 | [output appendFormat:@"%02x", digest[i]]; 101 | } 102 | return output; 103 | } 104 | 105 | @end 106 | -------------------------------------------------------------------------------- /control: -------------------------------------------------------------------------------- 1 | Package: com.jerrytian.shortlook.plugin.contact-photo.wechat 2 | Name: WeChat Profile Pictures for ShortLook 3 | Depends: mobilesubstrate, co.dynastic.ios.tweak.shortlook 4 | Version: 1.0.2 5 | Architecture: iphoneos-arm 6 | Description: Show WeChat users' profile pictures in ShortLook when you receive a WeChat notification! 7 | Maintainer: Jianan Tian 8 | Author: Jianan Tian 9 | Section: Tweaks (ShortLook) 10 | --------------------------------------------------------------------------------