├── .gitignore ├── LICENSE ├── README.md ├── config.ini ├── config.py ├── config_files ├── nginx │ ├── tgbot.conf │ └── vps.conf └── supervisor │ ├── bot.conf │ └── sms.conf ├── data ├── Sketch.png ├── Sketch2.png ├── Sketch3.png ├── Sketch4.png ├── dump └── last ├── relay.py ├── tg_callback.py └── utils ├── TG_Bot.py ├── __init__.py ├── aws.py ├── contact_book.py ├── misc.py ├── reboot.sh ├── setwebhooks.py ├── sms.py └── vcf_convertor.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 ihc童鞋@提不起劲 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android SMS Relay 2 | Script to forward android(with root) sms using adb 3 | 4 | Useful when you have another phone number but unwilling to carry 2 phones out. 5 | 6 | ### Features 7 | 8 | - Forward sms to telegram chat. 9 | - Match contacts automatically. 10 | - Send sms using telegram. 11 | - Some useful features: 12 | - Query phone number(You can import contacts from iCloud). 13 | - Query curriculum. 14 | - Dual sim card to different users. 15 | - Reply telegram message to reply sms quickly. 16 | 17 | 18 | ### Prerequisite 19 | 20 | - An android device with root and debug mode open. 21 | - I use an android 4.0.4 because of poor, on other versions you should check the debug and lock screen settings. 22 | - A linux server(Rpi for example) with adb tools installed. 23 | - Proper network connection 24 | - I use shadowsocks to provide a socks5 proxy for connecting to telegram server due to the GFW. 25 | - Also a tinc VPN is used to forward requests to inner server: 26 | - Flask ===SOCK FILE=== nginx(Rpi, Fudan Uni.) ===TINC=== VPS nginx(AWS, Seoul) 27 | - Python 2.7, requests, requests[socks], flask, uwsgi, nginx, supervisor 28 | 29 | 30 | ### How it works 31 | 32 | - Pull the database every several seconds, then check new sms from it. This process only cost about 0.01 sec, so it's fine. 33 | - Set a webhook to telegram bot, then it can receive message sent by the user. 34 | - Simulate key press when entering PIN code after reboot. 35 | 36 | ![look1](data/Sketch.png) 37 | (Send and receive sms, contacts matching automatically) 38 | 39 |
40 | 41 | ![look2](data/Sketch2.png) 42 | (Query contacts) 43 | 44 |
45 | 46 | 47 | ![look3](data/Sketch3.png) 48 | (Quick reply && Message for sending status) 49 | 50 |
51 | 52 | 53 | ![look3](data/Sketch4.png) 54 | (Message update to sent status) 55 | -------------------------------------------------------------------------------- /config.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | wsgi-file = /opt/sms/tg_callback.py 3 | callable = app 4 | #http = 192.168.102.130:11100 5 | socket = /opt/sms/flask.sock 6 | chmod-socket = 666 7 | processes = 1 8 | threads = 1 9 | no-site=true 10 | pythonpath = /usr/local/lib/python2.7/dist-packages 11 | #pythonpath = /root/.local/lib/python2.7/site-packages 12 | #pythonpath = /usr/lib/python2.7/dist-packages 13 | #pythonpath = /usr/local/lib/python2.7/site-packages 14 | buffer-size = 32768 15 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Your telegram token (Create one if you haven't got it: https://telegram.me/botfather) 4 | TG_TOKEN = "YOUR_TELEGRAM_TOKEN" 5 | CHAT_ID = [259444514, 259444514] # Your chat id to yourself (2 items for dual sim card) 6 | 7 | KB_COMMAND = ["kb", u"课表"] 8 | KB_IMAGE = "https://unknown" 9 | KB_IMAGE_CAPTION = "小海的课表" 10 | 11 | FIND_CONTACT_COMMAND = ["search", "contact", "num"] # Commands to query contact book 12 | 13 | AWS_COMMAND = ["aws", u"流量"] 14 | 15 | SMS_COMMAND = ["sms"] # Commands to send sms 16 | 17 | # Proxy to connect to telegram 18 | USE_PROXY = True 19 | _PROXY = dict(http='socks5://127.0.0.1:16801', https='socks5://127.0.0.1:16801') 20 | PROXY = None if not USE_PROXY else _PROXY 21 | 22 | CONTACT = "/opt/sms/data/dump" # Path to contact json file 23 | MEM_SAVE_PATH = "/dev/shm/mmssms.db" # DB saving path when /dev/shm is available 24 | SAVE_PATH = "/tmp/mmssms.db" # DB saving path when /dev/shm is not available 25 | LAST_FILE = "/opt/sms/data/last" # File to save the time of last checking 26 | LOCK = "/dev/shm/sms-lock" 27 | MAX_LOCK_WAIT = 20 28 | 29 | FREE_SIZE = 50 30 | CHECK_INTERVAL = 10 31 | -------------------------------------------------------------------------------- /config_files/nginx/tgbot.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 192.168.102.130:11100; 3 | server_name ihc.im default_server; 4 | charset utf-8; 5 | client_max_body_size 75M; 6 | location / { try_files $uri @yourapplication; } 7 | location @yourapplication { 8 | include uwsgi_params; 9 | uwsgi_pass unix:/opt/sms/flask.sock; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /config_files/nginx/vps.conf: -------------------------------------------------------------------------------- 1 | # Add this to your VPS nginx conf(NOT overwrite) to forward telegram requests to inner server. 2 | 3 | location = /YOUR_TG_TOKEN { 4 | proxy_pass http://192.168.102.130:11100; 5 | } 6 | -------------------------------------------------------------------------------- /config_files/supervisor/bot.conf: -------------------------------------------------------------------------------- 1 | [program:bot] 2 | directory = /opt/sms 3 | command = /usr/local/bin/uwsgi -c /opt/sms/config.ini 4 | autostart = true 5 | autorestart = true 6 | startsecs = 5 7 | startretries = 30 8 | user = root 9 | 10 | -------------------------------------------------------------------------------- /config_files/supervisor/sms.conf: -------------------------------------------------------------------------------- 1 | [program:sms] 2 | directory = /opt/sms 3 | command = /usr/bin/python /opt/sms/relay.py 4 | autostart = true 5 | autorestart = true 6 | startsecs = 5 7 | startretries = 30 8 | user = root 9 | 10 | -------------------------------------------------------------------------------- /data/Sketch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihciah/AndroidSMSRelay/3e8cc1842bc3b72adf446e4f6f88e559529da38c/data/Sketch.png -------------------------------------------------------------------------------- /data/Sketch2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihciah/AndroidSMSRelay/3e8cc1842bc3b72adf446e4f6f88e559529da38c/data/Sketch2.png -------------------------------------------------------------------------------- /data/Sketch3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihciah/AndroidSMSRelay/3e8cc1842bc3b72adf446e4f6f88e559529da38c/data/Sketch3.png -------------------------------------------------------------------------------- /data/Sketch4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ihciah/AndroidSMSRelay/3e8cc1842bc3b72adf446e4f6f88e559529da38c/data/Sketch4.png -------------------------------------------------------------------------------- /data/dump: -------------------------------------------------------------------------------- 1 | {"10001": "China Telecom", "10000": "China Telecom"} 2 | -------------------------------------------------------------------------------- /data/last: -------------------------------------------------------------------------------- 1 | 1505196857314 -------------------------------------------------------------------------------- /relay.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import datetime 4 | import os 5 | import sqlite3 6 | import time 7 | 8 | from config import CHECK_INTERVAL, CONTACT 9 | from utils.TG_Bot import TGBot 10 | from utils.contact_book import Contact 11 | from utils.misc import LastFile, clean_env, get_db_save_path 12 | 13 | __author__ = 'ihciah' 14 | 15 | contact_book = Contact(CONTACT) 16 | last_time = LastFile() 17 | 18 | 19 | def send_telegram(address, date, date_sent, body, user=0): 20 | message = u"%s\n\nRecv at %s\nSend at %s\n\nSender: %s (%s)" 21 | date, date_sent = [datetime.datetime.fromtimestamp(int(d) / 1000).strftime('%m-%d %H:%M:%S') 22 | for d in (date, date_sent)] 23 | ret, sender_name = contact_book.num2name(address) 24 | if not ret: 25 | sender_name = "No matching contact" 26 | result = None 27 | try: 28 | result = TGBot.send_message(message % (body, date, date_sent, address, sender_name), user) 29 | except: 30 | pass 31 | return result 32 | 33 | 34 | def read_db(): 35 | db_path = get_db_save_path() 36 | DOWNLOAD_COMMAND = "/usr/bin/adb pull /data/data/com.android.providers.telephony/databases/mmssms.db %s" 37 | os.system(DOWNLOAD_COMMAND % db_path) 38 | if not os.path.isfile(db_path) or os.path.getsize(db_path) == 0: 39 | clean_env() 40 | return 41 | conn = sqlite3.connect(db_path) 42 | cursor = conn.cursor() 43 | SQL = 'SELECT _id,address,date,date_sent,body,sub_id FROM sms WHERE date>%d AND type=1 ORDER BY date ASC' 44 | cursor.execute(SQL % last_time.get_last_time()) 45 | values = cursor.fetchall() 46 | print values 47 | for _id, address, date, date_sent, body, sub_id in values: 48 | if send_telegram(address, date, date_sent, body, sub_id) is not None: 49 | last_time.update_time(date) 50 | conn.close() 51 | clean_env() 52 | 53 | if __name__ == "__main__": 54 | while True: 55 | try: 56 | read_db() 57 | except: 58 | pass 59 | time.sleep(CHECK_INTERVAL) 60 | -------------------------------------------------------------------------------- /tg_callback.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from flask import Flask, request 4 | from config import KB_COMMAND, KB_IMAGE_CAPTION, FIND_CONTACT_COMMAND, SMS_COMMAND, AWS_COMMAND 5 | from config import TG_TOKEN, KB_IMAGE, CHAT_ID, CONTACT 6 | from utils.TG_Bot import TGBot 7 | from utils.contact_book import Contact 8 | from utils.misc import FileLock 9 | from utils.sms import send_sms, reply_sms 10 | from utils.aws import get_used_bandwidth 11 | 12 | __author__ = 'ihciah' 13 | 14 | contact = Contact(CONTACT) 15 | 16 | app = Flask(__name__) 17 | 18 | 19 | def parse_authorized_message(message): 20 | if 'text' not in message: 21 | return 22 | original_text = message['text'].strip() 23 | card = CHAT_ID.index(message['chat']['id']) 24 | 25 | # Send sms 26 | text = original_text.split(" ", 2) 27 | if len(text) >= 3 and text[0] in SMS_COMMAND: 28 | send_sms(text[1], text[2], card) 29 | return True 30 | 31 | # Reply sms 32 | if 'reply_to_message' in message and len(original_text): 33 | reply_sms(message['reply_to_message'], original_text, card) 34 | return True 35 | 36 | # Lookup contact 37 | text = original_text.split(" ", 1) 38 | if len(text) == 2 and text[0] in FIND_CONTACT_COMMAND: 39 | contact.send_contact(text[1], card) 40 | return True 41 | 42 | # KB command 43 | text = original_text 44 | if text in KB_COMMAND: 45 | TGBot.send_image(KB_IMAGE, KB_IMAGE_CAPTION) 46 | return True 47 | 48 | # AWS bandwidth command 49 | text = original_text 50 | if text in AWS_COMMAND: 51 | message_id = TGBot.send_message("[Working] Query AWS bandwidth...", card) 52 | used = get_used_bandwidth() / (1000 ** 3) 53 | TGBot.update_message("AWS bandwidth used: %.2f GB / 15 GB" % used, message_id, card) 54 | return True 55 | 56 | return False 57 | 58 | 59 | def parse_normal_message(message): 60 | if 'text' not in message: 61 | return 62 | chat_id = message['chat']['id'] 63 | 64 | # Reply chat ID 65 | if message['text'] == "id": 66 | TGBot.send_message(str(chat_id), chat_id) 67 | else: 68 | TGBot.send_message("Command not found:\n%s" % message['text'], chat_id) 69 | 70 | 71 | def handle_message(msg): 72 | message = msg['message'] 73 | flag = False 74 | if message['chat']['id'] in CHAT_ID: 75 | FileLock.wait_lock() 76 | try: 77 | FileLock.create_lock() 78 | flag = parse_authorized_message(message) 79 | finally: 80 | FileLock.delete_lock() 81 | if not flag: 82 | parse_normal_message(message) 83 | 84 | 85 | @app.route('/'+TG_TOKEN, methods=['POST']) 86 | def recv(): 87 | j = request.get_json(force=True) 88 | try: 89 | handle_message(j) 90 | except: 91 | pass 92 | return "ok", 200 93 | 94 | if __name__ == '__main__': 95 | app.run(host="192.168.102.130", port=11100) 96 | -------------------------------------------------------------------------------- /utils/TG_Bot.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import requests 4 | from config import TG_TOKEN, USE_PROXY, PROXY, CHAT_ID 5 | 6 | __author__ = 'ihciah' 7 | 8 | 9 | class TGBot: 10 | @staticmethod 11 | def send_message(text, user=0): 12 | tg_message_url = "https://api.telegram.org/bot%s/sendMessage" % TG_TOKEN 13 | user = int(user) 14 | data = {"chat_id": CHAT_ID[user] if user < len(CHAT_ID) else user, 15 | "text": text, 16 | "disable_notification": False 17 | } 18 | res = requests.post(tg_message_url, data, proxies=PROXY).json() 19 | if res["ok"]: 20 | return res["result"]["message_id"] 21 | return None 22 | 23 | @staticmethod 24 | def send_image(image_url, caption="", user=0): 25 | tg_photo_url = "https://api.telegram.org/bot%s/sendPhoto" % TG_TOKEN 26 | user = int(user) 27 | data = {"chat_id": CHAT_ID[user] if user < len(CHAT_ID) else user, 28 | "photo": image_url, 29 | "disable_notification": False, 30 | } 31 | if caption: 32 | data["caption"] = caption 33 | requests.post(tg_photo_url, data, proxies=PROXY) 34 | 35 | @staticmethod 36 | def update_message(text, message_id, user=0): 37 | tg_update_url = "https://api.telegram.org/bot%s/editMessageText" % TG_TOKEN 38 | user = int(user) 39 | data = {"chat_id": CHAT_ID[user] if user < len(CHAT_ID) else user, 40 | "text": text, 41 | "message_id": message_id 42 | } 43 | requests.post(tg_update_url, data, proxies=PROXY) 44 | -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /utils/aws.py: -------------------------------------------------------------------------------- 1 | # -*-coding: utf-8 -*- 2 | __author__ = 'ihciah' 3 | 4 | from datetime import datetime 5 | from dateutil.tz import tzutc 6 | 7 | def get_used_bandwidth(): 8 | import boto3 9 | now = datetime.utcnow() 10 | client = boto3.client('cloudwatch') 11 | response = client.get_metric_statistics( 12 | Namespace='AWS/EC2', 13 | MetricName='NetworkOut', 14 | Dimensions=[ 15 | { 16 | 'Name': 'InstanceId', 17 | 'Value': 'i-068fb997b5721188c' 18 | }, 19 | ], 20 | StartTime=datetime(now.year, now.month, 1, 0, 0, tzinfo=tzutc()), 21 | EndTime=datetime(now.year, now.month, now.day, 23, 59, tzinfo=tzutc()), 22 | Period=31*24*3600, 23 | Statistics=[ 24 | 'Sum', 25 | ], 26 | #Unit='Gigabytes' 27 | ) 28 | return response['Datapoints'][0]['Sum'] 29 | 30 | if __name__ == "__main__": 31 | used = get_used_bandwidth() 32 | print "%.2f GB" % (used/(1000**3)) 33 | -------------------------------------------------------------------------------- /utils/contact_book.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | import json 5 | from utils.TG_Bot import TGBot 6 | 7 | __author__ = 'ihciah' 8 | 9 | 10 | class Contact: 11 | def __init__(self, path="data/dump"): 12 | self.book = {} 13 | if os.path.isfile(path): 14 | with open(path) as f: 15 | try: 16 | d = json.loads(f.read()) 17 | except: 18 | d = {} 19 | self.book = d 20 | self.rd = dict([(v.replace(" ", ""), k) for k, v in self.book.items()]) 21 | 22 | def num2name(self, number): 23 | number = str(number) 24 | if len(number) == 13: 25 | number = number[-11:] 26 | if number in self.book: 27 | return True, self.book[number] 28 | return False, "" 29 | 30 | def search_name(self, name): 31 | ret = [] 32 | if isinstance(name, (str, unicode)): 33 | name = [name] 34 | # if len(name) == 1 and name[0] in self.rd: 35 | # ret.append([name, self.rd[name]]) 36 | # return ret 37 | for w, num in self.rd.items(): 38 | for word in name: 39 | if w.find(word) == -1: 40 | break 41 | else: 42 | ret.append([w, num]) 43 | return ret 44 | 45 | def send_contact(self, names, card): 46 | result = self.search_name(names.split()) 47 | if result: 48 | result_message = ("Results for %s:\n" % names) + "\n".join(["%s %s" % (who, num) for who, num in result]) 49 | TGBot.send_message(result_message, card) 50 | else: 51 | TGBot.send_message("No result for %s" % names, card) 52 | -------------------------------------------------------------------------------- /utils/misc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | import time 5 | 6 | from config import MEM_SAVE_PATH, FREE_SIZE, SAVE_PATH, LAST_FILE, MAX_LOCK_WAIT, LOCK 7 | 8 | __author__ = 'ihciah' 9 | 10 | 11 | def clean_env(): 12 | to_delete = ['/dev/shm/mmssms.db', '/dev/shm/mmssms.db-journal', 13 | '/tmp/mmssms.db', '/tmp/mmssms.db-journal'] 14 | for f in to_delete: 15 | if os.path.exists(f): 16 | os.remove(f) 17 | 18 | 19 | def get_db_save_path(): 20 | statvfs = os.statvfs('/dev/shm') 21 | free = statvfs.f_frsize * statvfs.f_bavail / 1024 / 1024 22 | return MEM_SAVE_PATH if free > FREE_SIZE else SAVE_PATH 23 | 24 | 25 | class LastFile: 26 | def __init__(self): 27 | self.last_time = self.get_last_time_on_disk() 28 | 29 | def get_last_time_on_disk(self): 30 | if os.path.isfile(LAST_FILE): 31 | with open(LAST_FILE) as f: 32 | last = f.read().strip() 33 | if last.isdigit() and len(last) > 9: 34 | return int(last) 35 | last = int(time.time() * 1000) 36 | self.dump_to_disk(last) 37 | return last 38 | 39 | def get_last_time(self): 40 | return self.last_time 41 | 42 | def dump_to_disk(self, t): 43 | with open(LAST_FILE, "w") as fw: 44 | fw.write(str(t)) 45 | 46 | def update_time(self, t): 47 | if self.last_time >= t: 48 | return 49 | self.last_time = t 50 | self.dump_to_disk(t) 51 | 52 | 53 | class FileLock: 54 | @staticmethod 55 | def wait_lock(): 56 | wait_time = 0.0 57 | while True: 58 | if wait_time > MAX_LOCK_WAIT: 59 | FileLock.delete_lock() 60 | if os.path.isfile(LOCK): 61 | time.sleep(0.5) 62 | wait_time += 0.5 63 | else: 64 | break 65 | 66 | @staticmethod 67 | def create_lock(): 68 | open(LOCK, 'a').close() 69 | 70 | @staticmethod 71 | def delete_lock(): 72 | try: 73 | os.remove(LOCK) 74 | finally: 75 | pass 76 | -------------------------------------------------------------------------------- /utils/reboot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Script for input PIN code of SIM card(0000 in this case) when rebooting. 3 | # Use this only when you open the PIN lock on your SIM card. 4 | adb shell reboot 5 | sleep 45 6 | adb shell input text 0000 7 | sleep 2 8 | adb shell input keyevent 66 9 | 10 | -------------------------------------------------------------------------------- /utils/setwebhooks.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Script to set telegram webhooks 3 | 4 | import requests 5 | from config import PROXY, TG_TOKEN 6 | 7 | __author__ = 'ihciah' 8 | 9 | CALLBACK = "https://ihc.im/" + TG_TOKEN # Modify this url to your callback url. 10 | 11 | url = "https://api.telegram.org/bot%s/setWebhook" % TG_TOKEN 12 | res = requests.post(url, {"url": CALLBACK}, proxies=PROXY) 13 | print res.content 14 | -------------------------------------------------------------------------------- /utils/sms.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import subprocess 3 | import re 4 | 5 | from utils.TG_Bot import TGBot 6 | 7 | __author__ = 'ihciah' 8 | 9 | 10 | def sms_escape(sms): 11 | sms = sms.replace("\"", "\\\"").replace("\\", "\\\\") 12 | sms = sms.replace("`", "\\`") 13 | return sms 14 | 15 | 16 | def send_sms(number, content, card): 17 | receiver = str(number).replace("+", "").replace(".", "") 18 | if receiver[:2] in ["13", "18", "17", "15", "14"] and len(receiver) == 11: 19 | receiver = "86" + receiver 20 | if not receiver.isdigit(): 21 | TGBot.send_message("Number %s is invalid." % number, card) 22 | return 23 | sms = content 24 | sms = sms_escape(sms) 25 | 26 | message_id = TGBot.send_message("[Working] Sending message to %s:\n%s" % (receiver, content), card) 27 | 28 | if card == 0: 29 | # Send message using default sim card 30 | subprocess.call(['adb', 'shell', 'service', 'call', 'isms', '5', 's16', 31 | receiver, 'i32', '0', 'i32', '0', 's16', sms 32 | ]) 33 | else: 34 | # Send message using secondary sim card 35 | # 61: Tab; 66: Enter; 20: Arrow_Down; 3: Home 36 | subprocess.call(['adb', 'shell', 'am', 'start', '-a', 'android.intent.action.SENDTO', 37 | '-d', 'sms:%s' % receiver, '--es', 'sms_body', '\"%s\"' % sms, 38 | '--ez', 'exit_on_sent', 'true', '-S']) 39 | subprocess.call(['sleep', '3']) 40 | exec_keycode = [61, 66, 20, 66, 3] 41 | for key in exec_keycode: 42 | subprocess.call(['adb', 'shell', 'input', 'keyevent', str(key)]) 43 | subprocess.call(['sleep', '1']) 44 | 45 | if message_id is not None: 46 | TGBot.update_message("SMS to %s has been sent:\n%s" % (receiver, content), message_id, card) 47 | else: 48 | TGBot.send_message("SMS to %s has been sent:\n%s" % (receiver, content), card) 49 | 50 | 51 | def reply_sms(message_to_reply, sms_content, card): 52 | if 'text' in message_to_reply: 53 | reply_text = message_to_reply['text'] 54 | last_line = reply_text.split("\n")[-1] 55 | pattern = re.compile(r'(\d{3,})') 56 | m = re.search(pattern, last_line) 57 | if m: 58 | send_sms(m.group(0), sms_content, card) 59 | return 60 | TGBot.send_message("Cannot reply message!", card) 61 | -------------------------------------------------------------------------------- /utils/vcf_convertor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import re 4 | import json 5 | 6 | __author__ = 'ihciah' 7 | 8 | person_patten = re.compile(r'BEGIN:VCARD(.*?)END:VCARD', re.DOTALL) 9 | fullname_patten = re.compile(r'FN:(.*?)\n') 10 | mobile_patten = re.compile(r':\+*?(\d{9}\d*?)\n') 11 | 12 | f = open(r'data/iCloud vCard.vcf') 13 | fc = f.read() 14 | people = person_patten.findall(fc) 15 | f.close() 16 | 17 | names = {} 18 | for p in people: 19 | for i in fullname_patten.findall(p): 20 | name = i 21 | if len(name.strip()) == 0: 22 | continue 23 | p = p.replace("-", "") 24 | for i in mobile_patten.findall(p): 25 | if len(i) == 13 and i[:2] == "86": 26 | i = i[2:] 27 | names[i] = name 28 | 29 | fl = open("dump", "w") 30 | fl.write(json.dumps(names)) 31 | fl.close() 32 | --------------------------------------------------------------------------------