Repository: wangshub/Douyin-Bot
Branch: master
Commit: 399c19b662ac
Files: 25
Total size: 24.4 KB
Directory structure:
gitextract_w5uw559i/
├── .gitignore
├── LICENSE
├── README.md
├── Tools/
│ └── README.md
├── apk/
│ ├── ADBKeyBoard.apk
│ └── aapt
├── common/
│ ├── UnicodeStreamFilter.py
│ ├── __init__.py
│ ├── apiutil.py
│ ├── auto_adb.py
│ ├── compression.py
│ ├── config.py
│ ├── debug.py
│ ├── excel_keyword.py
│ └── screenshot.py
├── config/
│ ├── 1280x720/
│ │ └── config.json
│ ├── 1920x1080/
│ │ └── config.json
│ └── default.json
├── douyin-bot.py
├── example/
│ ├── test_crop.py
│ ├── test_plot.py
│ ├── test_readExcel.py
│ └── test_textInput.py
├── reply/
│ └── data.json
└── requirements.txt
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.idea/
.DS_Store
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# pyenv
.python-version
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
face/
autojump.png
optimized.png
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018 神奇的战士-王松
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# 如何在抖音上找到漂亮小姐姐----抖音机器人
[](https://github.com/ellerbrock/open-source-badge/) [](https://opensource.org/licenses/mit-license.php)
最近沉迷于抖音无法自拔,常常花好几个小时在抖音**漂亮小姐姐**身上。
本着**高效、直接**地找到漂亮小姐姐的核心思想,我用 Python + ADB 做了一个 Python 抖音机器人 Douyin-Bot。
<img src="./screenshot/demo.gif" title="Logo" width="300"> <img src="./screenshot/auto_reply.gif" title="Logo" width="300">
## 特性
- [x] **自动翻页**
- [x] **颜值检测**
- [x] **人脸识别**
- [x] **自动点赞**
- [x] **自动关注**
- [x] 随机防 Ban
- [x] **自动评论**
## 原理
- 打开《抖音短视频》APP,进入主界面
- 获取手机截图,并对截图进行压缩 (Size < 1MB);
- 请求 [人脸识别 API](http://ai.qq.com/);
- 解析返回的人脸 Json 信息,对人脸检测切割;
- 当颜值大于门限值 `BEAUTY_THRESHOLD`时,点赞并关注;
- 下一页,返回第一步;
## 使用教程
- Python版本:3.0及以上
- 相关软件工具安装和使用步骤请参考 [wechat_jump_game](https://github.com/wangshub/wechat_jump_game) 和 [Android 操作步骤](https://github.com/wangshub/wechat_jump_game/wiki/Android-%E5%92%8C-iOS-%E6%93%8D%E4%BD%9C%E6%AD%A5%E9%AA%A4)
- 在 [ai.qq.com](https://ai.qq.com) 免费申请 `AppKey` 和 `AppID`
1. 获取源码:`git clone https://github.com/wangshub/Douyin-Bot.git`
2. 进入源码目录: `cd Douyin-Bot`
3. 安装依赖: `pip install -r requirements.txt`
4. 运行程序:`python douyin-bot.py`
5. [自动评论](https://zhuanlan.zhihu.com/p/57242891)(可选):`python3 douyin-bot.py --reply`
## 注意
- 目前暂时只适配了 一加5(1920x1080 分辨率),如果手机不是该分辨率,请修改 `config/` 文件夹下面的配置文件;
- `config.json`配置文件参考:
- `center_point`: 屏幕中心点`(x, y)`,区域范围 `rx, ry`
- `follow_bottom`: 关注按钮位置`(x, y)`,区域范围 `rx, ry`
- `star_bottom`: 点赞按钮位置`(x, y)`,区域范围 `rx, ry`
## 脸部截取

## LICENSE
MIT
欢迎 Star 和 Fork ~
如果你有什么问题请提 Issue,或者关注我的微信公众号留言,我都会一一解答
<p align="center">
<img src="screenshot/qrcode.jpg" title="Logo" width="150">
</>
================================================
FILE: Tools/README.md
================================================
## 所有实验都在该文件夹下运行即可,已放上测试用的代码。免去配置 adb 的麻烦
- 复制 wechat_jump_game 根目录下的 config 文件夹以及 wechat_jump_py3.py 文件到本目录下
- 按住 `shift` + 右键 选择在该文件夹下打开命令窗口(Windows 用户请自行 cmd)
- 打开安卓手机的 usb 调试,并连接电脑,在终端输入 `adb devices` 进行测试,如果有连接设备号则表示成功
- 打开微信小游戏,然后运行代码 `python wechat_jump_py3.py`,点击出现的图形起点和终点,棋子自动跳转
**注意:这里使用的是不需要配置的 adb 方式,需要在该文件下操作,至于如何自动跳转,只需改变执行脚本即可,这里只做演示**
================================================
FILE: common/UnicodeStreamFilter.py
================================================
# -*- coding: utf-8 -*-
import sys
if sys.version_info.major != 3:
class UnicodeStreamFilter:
def __init__(self, target):
self.target = target
self.encoding = 'utf-8'
self.errors = 'replace'
self.encode_to = self.target.encoding
def write(self, s):
if type(s) == str:
s = s.decode("utf-8")
s = s.encode(self.encode_to, self.errors).decode(self.encode_to)
self.target.write(s)
if sys.stdout.encoding == 'cp936':
sys.stdout = UnicodeStreamFilter(sys.stdout)
else:
pass
================================================
FILE: common/__init__.py
================================================
================================================
FILE: common/apiutil.py
================================================
#-*- coding: UTF-8 -*-
import hashlib
import urllib
from urllib import parse
import urllib.request
import base64
import json
import time
url_preffix='https://api.ai.qq.com/fcgi-bin/'
def setParams(array, key, value):
array[key] = value
def genSignString(parser):
uri_str = ''
for key in sorted(parser.keys()):
if key == 'app_key':
continue
uri_str += "%s=%s&" % (key, parse.quote(str(parser[key]), safe=''))
sign_str = uri_str + 'app_key=' + parser['app_key']
hash_md5 = hashlib.md5(sign_str.encode('utf-8'))
return hash_md5.hexdigest().upper()
class AiPlat(object):
def __init__(self, app_id, app_key):
self.app_id = app_id
self.app_key = app_key
self.data = {}
self.url_data = ''
def invoke(self, params):
self.url_data = urllib.parse.urlencode(params).encode("utf-8")
req = urllib.request.Request(self.url, self.url_data)
try:
rsp = urllib.request.urlopen(req)
str_rsp = rsp.read().decode('utf-8')
dict_rsp = json.loads(str_rsp)
return dict_rsp
except Exception as e:
print(e)
return {'ret': -1}
def face_detectface(self, image, mode):
self.url = url_preffix + 'face/face_detectface'
setParams(self.data, 'app_id', self.app_id)
setParams(self.data, 'app_key', self.app_key)
setParams(self.data, 'mode', mode)
setParams(self.data, 'time_stamp', int(time.time()))
setParams(self.data, 'nonce_str', int(time.time()))
image_data = base64.b64encode(image)
setParams(self.data, 'image', image_data.decode("utf-8"))
sign_str = genSignString(self.data)
setParams(self.data, 'sign', sign_str)
return self.invoke(self.data)
================================================
FILE: common/auto_adb.py
================================================
# -*- coding: utf-8 -*-
import os
import subprocess
import platform
class auto_adb():
def __init__(self):
try:
adb_path = 'adb'
subprocess.Popen([adb_path], stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
self.adb_path = adb_path
except OSError:
if platform.system() == 'Windows':
adb_path = os.path.join('Tools', "adb", 'adb.exe')
try:
subprocess.Popen(
[adb_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
self.adb_path = adb_path
except OSError:
pass
else:
try:
subprocess.Popen(
[adb_path], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
except OSError:
pass
print('请安装 ADB 及驱动并配置环境变量')
exit(1)
def get_screen(self):
process = os.popen(self.adb_path + ' shell wm size')
output = process.read()
return output
def run(self, raw_command):
print(raw_command)
command = '{} {}'.format(self.adb_path, raw_command)
process = os.popen(command)
output = process.read()
return output
def test_device(self):
print('检查设备是否连接...')
command_list = [self.adb_path, 'devices']
process = subprocess.Popen(command_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output = process.communicate()
if output[0].decode('utf8') == 'List of devices attached\n\n':
print('未找到设备')
print('adb 输出:')
for each in output:
print(each.decode('utf8'))
exit(1)
print('设备已连接')
print('adb 输出:')
for each in output:
print(each.decode('utf8'))
def test_density(self):
process = os.popen(self.adb_path + ' shell wm density')
output = process.read()
return output
def test_device_detail(self):
process = os.popen(self.adb_path + ' shell getprop ro.product.device')
output = process.read()
return output
def test_device_os(self):
process = os.popen(self.adb_path + ' shell getprop ro.build.version.release')
output = process.read()
return output
def adb_path(self):
return self.adb_path
================================================
FILE: common/compression.py
================================================
from PIL import Image
import math
import os
def resize_image(origin_img, optimize_img, threshold):
"""
shrink image by size
:param origin_img:
:param optimize_img:
:param threshold:
:return:
"""
file_size = os.path.getsize(origin_img)
with Image.open(origin_img) as im:
if file_size > threshold:
width, height = im.size
if width >= height:
new_width = int(math.sqrt(threshold / 2))
new_height = int(new_width * height * 1.0 / width)
else:
new_height = int(math.sqrt(threshold / 2))
new_width = int(new_height * width * 1.0 / height)
resized_im = im.resize((new_width, new_height))
resized_im.save(optimize_img)
else:
im.save(optimize_img)
================================================
FILE: common/config.py
================================================
# -*- coding: utf-8 -*-
"""
调取配置文件和屏幕分辨率的代码
"""
import os
import sys
import json
import re
from common.auto_adb import auto_adb
adb = auto_adb()
def open_accordant_config():
"""
调用配置文件
"""
screen_size = _get_screen_size()
config_file = "{path}/config/{screen_size}/config.json".format(
path=sys.path[0],
screen_size=screen_size
)
# 优先获取执行文件目录的配置文件
here = sys.path[0]
for file in os.listdir(here):
if re.match(r'(.+)\.json', file):
file_name = os.path.join(here, file)
with open(file_name, 'r') as f:
print("Load config file from {}".format(file_name))
return json.load(f)
# 根据分辨率查找配置文件
if os.path.exists(config_file):
with open(config_file, 'r') as f:
print("正在从 {} 加载配置文件".format(config_file))
return json.load(f)
else:
with open('{}/config/default.json'.format(sys.path[0]), 'r') as f:
print("Load default config")
return json.load(f)
def _get_screen_size():
"""
获取手机屏幕大小
"""
size_str = adb.get_screen()
m = re.search(r'(\d+)x(\d+)', size_str)
if m:
return "{height}x{width}".format(height=m.group(2), width=m.group(1))
return "1920x1080"
================================================
FILE: common/debug.py
================================================
# -*- coding: utf-8 -*-
"""
这是debug的代码,当DEBUG_SWITCH开关开启的时候,会将各种信息存在本地,方便检查故障
"""
import os
import sys
import shutil
import math
from PIL import ImageDraw
import platform
if platform.system() == 'Windows':
os.chdir(os.getcwd().replace('\\common', ''))
path_split = "\\"
else:
os.chdir(os.getcwd().replace('/common', ''))
path_split = '/'
# from common import ai
try:
from common.auto_adb import auto_adb
except ImportError as ex:
print(ex)
print('请将脚本放在项目根目录中运行')
print('请检查项目根目录中的 common 文件夹是否存在')
exit(1)
screenshot_backup_dir = 'screenshot_backups'
adb = auto_adb()
def make_debug_dir(screenshot_backup_dir):
"""
创建备份文件夹
"""
if not os.path.isdir(screenshot_backup_dir):
os.mkdir(screenshot_backup_dir)
def backup_screenshot(ts):
"""
为了方便失败的时候 debug
"""
make_debug_dir(screenshot_backup_dir)
shutil.copy('{}{}autojump.png'.format(os.getcwd(), path_split),
os.path.join(os.getcwd(), screenshot_backup_dir,
str(ts) + '.png'))
def save_debug_screenshot(ts, im, piece_x, piece_y, board_x, board_y):
"""
对 debug 图片加上详细的注释
"""
make_debug_dir(screenshot_backup_dir)
draw = ImageDraw.Draw(im)
draw.line((piece_x, piece_y) + (board_x, board_y), fill=2, width=3)
draw.line((piece_x, 0, piece_x, im.size[1]), fill=(255, 0, 0))
draw.line((0, piece_y, im.size[0], piece_y), fill=(255, 0, 0))
draw.line((board_x, 0, board_x, im.size[1]), fill=(0, 0, 255))
draw.line((0, board_y, im.size[0], board_y), fill=(0, 0, 255))
draw.ellipse((piece_x - 10, piece_y - 10, piece_x + 10, piece_y + 10), fill=(255, 0, 0))
draw.ellipse((board_x - 10, board_y - 10, board_x + 10, board_y + 10), fill=(0, 0, 255))
del draw
im.save(os.path.join(os.getcwd(), screenshot_backup_dir,
'#' + str(ts) + '.png'))
def computing_error(last_press_time, target_board_x, target_board_y, last_piece_x, last_piece_y, temp_piece_x,
temp_piece_y):
"""
计算跳跃实际误差
"""
target_distance = math.sqrt(
(target_board_x - last_piece_x) ** 2 + (target_board_y - last_piece_y) ** 2) # 上一轮目标跳跃距离
actual_distance = math.sqrt((temp_piece_x - last_piece_x) ** 2 + (temp_piece_y - last_piece_y) ** 2) # 上一轮实际跳跃距离
jump_error_value = math.sqrt((target_board_x - temp_piece_x) ** 2 + (target_board_y - temp_piece_y) ** 2) # 跳跃误差
print(round(target_distance), round(jump_error_value), round(actual_distance), round(last_press_time))
''''# 将结果采集进学习字典
if last_piece_x > 0 and last_press_time > 0:
ai.add_data(round(actual_distance, 2), round(last_press_time))
# print(round(actual_distance), round(last_press_time))'''
def dump_device_info():
"""
显示设备信息
"""
size_str = adb.get_screen()
device_str = adb.test_device_detail()
phone_os_str = adb.test_device_os()
density_str = adb.test_density()
print("""**********
Screen: {size}
Density: {dpi}
Device: {device}
Phone OS: {phone_os}
Host OS: {host_os}
Python: {python}
**********""".format(
size=size_str.replace('\n', ''),
dpi=density_str.replace('\n', ''),
device=device_str.replace('\n', ''),
phone_os=phone_os_str.replace('\n', ''),
host_os=sys.platform,
python=sys.version
))
================================================
FILE: common/excel_keyword.py
================================================
import xlrd
import random
def get_random_keyword(filename):
"""
get random row of filename
:param filename:
:return:
"""
try:
with xlrd.open_workbook(filename) as data:
table = data.sheets()[0]
data_list = []
data_list.extend(table.col_values(0))
return data_list[random.randint(0, len(data_list))]
except Exception as error:
print(Exception)
return 'BRAVO'
if __name__ == '__main__':
reply = get_random_keyword('../reply/keyword.xlsx')
print(reply)
================================================
FILE: common/screenshot.py
================================================
# -*- coding: utf-8 -*-
"""
手机屏幕截图的代码
"""
import subprocess
import os
import sys
from PIL import Image
from io import StringIO
try:
from common.auto_adb import auto_adb
except Exception as ex:
print(ex)
print('请将脚本放在项目根目录中运行')
print('请检查项目根目录中的 common 文件夹是否存在')
exit(1)
adb = auto_adb()
# SCREENSHOT_WAY 是截图方法,经过 check_screenshot 后,会自动递减,不需手动修改
SCREENSHOT_WAY = 3
def pull_screenshot():
"""
获取屏幕截图,目前有 0 1 2 3 四种方法,未来添加新的平台监测方法时,
可根据效率及适用性由高到低排序
"""
global SCREENSHOT_WAY
if 1 <= SCREENSHOT_WAY <= 3:
process = subprocess.Popen(
adb.adb_path + ' shell screencap -p',
shell=True, stdout=subprocess.PIPE)
binary_screenshot = process.stdout.read()
if SCREENSHOT_WAY == 2:
binary_screenshot = binary_screenshot.replace(b'\r\n', b'\n')
elif SCREENSHOT_WAY == 1:
binary_screenshot = binary_screenshot.replace(b'\r\r\n', b'\n')
return Image.open(StringIO(binary_screenshot))
elif SCREENSHOT_WAY == 0:
adb.run('shell screencap -p /sdcard/autojump.png')
adb.run('pull /sdcard/autojump.png .')
return Image.open('./autojump.png')
def check_screenshot():
"""
检查获取截图的方式
"""
global SCREENSHOT_WAY
if os.path.isfile('autojump.png'):
try:
os.remove('autojump.png')
except Exception:
pass
if SCREENSHOT_WAY < 0:
print('暂不支持当前设备')
sys.exit()
try:
im = pull_screenshot()
im.load()
im.close()
print('采用方式 {} 获取截图'.format(SCREENSHOT_WAY))
except Exception:
SCREENSHOT_WAY -= 1
check_screenshot()
================================================
FILE: config/1280x720/config.json
================================================
{
"center_point":{
"x": 333,
"y": 700,
"rx": 10,
"ry": 300
},
"follow_bottom":{
"x": 648,
"y": 534,
"rx": 10,
"ry": 10
},
"star_bottom":{
"x": 641,
"y": 642,
"rx": 10,
"ry": 10
}
}
================================================
FILE: config/1920x1080/config.json
================================================
{
"center_point":{
"x": 540,
"y": 965,
"rx": 10,
"ry": 300
},
"follow_bottom":{
"x": 983,
"y": 854,
"rx": 10,
"ry": 10
},
"star_bottom":{
"x": 986,
"y": 994,
"rx": 10,
"ry": 10
},
"comment_bottom":{
"x": 1000,
"y": 1240,
"rx": 10,
"ry": 10
},
"comment_text":{
"x": 300,
"y": 1855,
"rx": 10,
"ry": 10
},
"comment_send":{
"x": 1010,
"y": 1690,
"rx": 10,
"ry": 10
}
}
================================================
FILE: config/default.json
================================================
{
"center_point":{
"x": 540,
"y": 965,
"rx": 10,
"ry": 300
},
"follow_bottom":{
"x": 990,
"y": 950,
"rx": 10,
"ry": 10
},
"star_bottom":{
"x": 1000,
"y": 1083,
"rx": 10,
"ry": 10
},
"comment_bottom":{
"x": 1000,
"y": 1240,
"rx": 10,
"ry": 10
},
"comment_text":{
"x": 300,
"y": 1855,
"rx": 10,
"ry": 10
},
"comment_send":{
"x": 1010,
"y": 1690,
"rx": 10,
"ry": 10
}
}
================================================
FILE: douyin-bot.py
================================================
# -*- coding: utf-8 -*-
import sys
import random
import time
from PIL import Image
import argparse
if sys.version_info.major != 3:
print('Please run under Python3')
exit(1)
try:
from common import debug, config, screenshot, UnicodeStreamFilter
from common.auto_adb import auto_adb
from common import apiutil
from common.compression import resize_image
except Exception as ex:
print(ex)
print('请将脚本放在项目根目录中运行')
print('请检查项目根目录中的 common 文件夹是否存在')
exit(1)
VERSION = "0.0.1"
# 我申请的 Key,随便用,嘻嘻嘻
# 申请地址 http://ai.qq.com
AppID = '1106858595'
AppKey = 'bNUNgOpY6AeeJjFu'
DEBUG_SWITCH = True
FACE_PATH = 'face/'
adb = auto_adb()
adb.test_device()
config = config.open_accordant_config()
# 审美标准
BEAUTY_THRESHOLD = 80
# 最小年龄
GIRL_MIN_AGE = 14
def yes_or_no():
"""
检查是否已经为启动程序做好了准备
"""
while True:
yes_or_no = str(input('请确保手机打开了 ADB 并连接了电脑,'
'然后打开手机软件,确定开始?[y/n]:'))
if yes_or_no == 'y':
break
elif yes_or_no == 'n':
print('谢谢使用')
exit(0)
else:
print('请重新输入')
def _random_bias(num):
"""
random bias
:param num:
:return:
"""
return random.randint(-num, num)
def next_page():
"""
翻到下一页
:return:
"""
cmd = 'shell input swipe {x1} {y1} {x2} {y2} {duration}'.format(
x1=config['center_point']['x'],
y1=config['center_point']['y']+config['center_point']['ry'],
x2=config['center_point']['x'],
y2=config['center_point']['y'],
duration=200
)
adb.run(cmd)
time.sleep(1.5)
def follow_user():
"""
关注用户
:return:
"""
cmd = 'shell input tap {x} {y}'.format(
x=config['follow_bottom']['x'] + _random_bias(10),
y=config['follow_bottom']['y'] + _random_bias(10)
)
adb.run(cmd)
time.sleep(0.5)
def thumbs_up():
"""
点赞
:return:
"""
cmd = 'shell input tap {x} {y}'.format(
x=config['star_bottom']['x'] + _random_bias(10),
y=config['star_bottom']['y'] + _random_bias(10)
)
adb.run(cmd)
time.sleep(0.5)
def tap(x, y):
cmd = 'shell input tap {x} {y}'.format(
x=x + _random_bias(10),
y=y + _random_bias(10)
)
adb.run(cmd)
def auto_reply():
msg = "垆边人似月,皓腕凝霜雪。就在刚刚,我的心动了一下,小姐姐你好可爱呀_Powered_By_Python"
# 点击右侧评论按钮
tap(config['comment_bottom']['x'], config['comment_bottom']['y'])
time.sleep(1)
#弹出评论列表后点击输入评论框
tap(config['comment_text']['x'], config['comment_text']['y'])
time.sleep(1)
#输入上面msg内容 ,注意要使用ADB keyboard 否则不能自动输入,参考: https://www.jianshu.com/p/2267adf15595
cmd = 'shell am broadcast -a ADB_INPUT_TEXT --es msg {text}'.format(text=msg)
adb.run(cmd)
time.sleep(1)
# 点击发送按钮
tap(config['comment_send']['x'], config['comment_send']['y'])
time.sleep(0.5)
# 触发返回按钮, keyevent 4 对应安卓系统的返回键,参考KEY 对应按钮操作: https://www.cnblogs.com/chengchengla1990/p/4515108.html
cmd = 'shell input keyevent 4'
adb.run(cmd)
def parser():
ap = argparse.ArgumentParser()
ap.add_argument("-r", "--reply", action='store_true',
help="auto reply")
args = vars(ap.parse_args())
return args
def main():
"""
main
:return:
"""
print('程序版本号:{}'.format(VERSION))
print('激活窗口并按 CONTROL + C 组合键退出')
debug.dump_device_info()
screenshot.check_screenshot()
cmd_args = parser()
while True:
next_page()
time.sleep(1)
screenshot.pull_screenshot()
resize_image('autojump.png', 'optimized.png', 1024*1024)
with open('optimized.png', 'rb') as bin_data:
image_data = bin_data.read()
ai_obj = apiutil.AiPlat(AppID, AppKey)
rsp = ai_obj.face_detectface(image_data, 0)
major_total = 0
minor_total = 0
if rsp['ret'] == 0:
beauty = 0
for face in rsp['data']['face_list']:
msg_log = '[INFO] gender: {gender} age: {age} expression: {expression} beauty: {beauty}'.format(
gender=face['gender'],
age=face['age'],
expression=face['expression'],
beauty=face['beauty'],
)
print(msg_log)
face_area = (face['x'], face['y'], face['x']+face['width'], face['y']+face['height'])
img = Image.open("optimized.png")
cropped_img = img.crop(face_area).convert('RGB')
cropped_img.save(FACE_PATH + face['face_id'] + '.png')
# 性别判断
if face['beauty'] > beauty and face['gender'] < 50:
beauty = face['beauty']
if face['age'] > GIRL_MIN_AGE:
major_total += 1
else:
minor_total += 1
# 是个美人儿~关注点赞走一波
if beauty > BEAUTY_THRESHOLD and major_total > minor_total:
print('发现漂亮妹子!!!')
thumbs_up()
follow_user()
if cmd_args['reply']:
auto_reply()
else:
print(rsp)
continue
if __name__ == '__main__':
try:
# yes_or_no()
main()
except KeyboardInterrupt:
adb.run('kill-server')
print('谢谢使用')
exit(0)
================================================
FILE: example/test_crop.py
================================================
from PIL import Image
im = Image.open("../autojump.png")
w, h = im.size
area = (0, 0, 50, 50)
im_croped = im.crop(area)
im_croped.show()
================================================
FILE: example/test_plot.py
================================================
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import numpy as np
# img = mpimg.imread('../screenshot/main_page.jpeg')
# img = mpimg.imread('../screenshot/main_page.jpeg')
# img = mpimg.imread('../screenshot/news_normal.jpeg')
# img = mpimg.imread('../screenshot/news_comment.jpeg')
# img = mpimg.imread('../screenshot/comment_comment.jpeg')
# img = mpimg.imread('../screenshot/add_comment.jpeg')
img = mpimg.imread('../autojump.png')
imgplot = plt.imshow(img)
plt.show()
================================================
FILE: example/test_readExcel.py
================================================
import xlrd
import random
#打开excel文件
data=xlrd.open_workbook('../reply/keyword.xlsx')
#获取第一张工作表(通过索引的方式)
table=data.sheets()[0]
#data_list用来存放数据
data_list=[]
#将table中第一行的数据读取并添加到data_list中
data_list.extend(table.col_values(0))
#打印出第一行的全部数据
for item in data_list:
print(item)
================================================
FILE: example/test_textInput.py
================================================
import os
def adb_keyboard_input(text):
"""
adb keyboard app input unicode text
:param text:
:return:
"""
cmd = 'adb shell am broadcast -a ADB_INPUT_TEXT --es msg {text}'.format(text=text.replace(' ', '%s'))
os.system(cmd)
adb_keyboard_input('hello world')
================================================
FILE: reply/data.json
================================================
{
"data":[
"你好,陌生人"
]
}
================================================
FILE: requirements.txt
================================================
matplotlib==2.2.0
xlrd==1.1.0
pandas==0.22.0
numpy==1.14.1
Pillow==5.1.0
scikit-learn==0.19.1
gitextract_w5uw559i/ ├── .gitignore ├── LICENSE ├── README.md ├── Tools/ │ └── README.md ├── apk/ │ ├── ADBKeyBoard.apk │ └── aapt ├── common/ │ ├── UnicodeStreamFilter.py │ ├── __init__.py │ ├── apiutil.py │ ├── auto_adb.py │ ├── compression.py │ ├── config.py │ ├── debug.py │ ├── excel_keyword.py │ └── screenshot.py ├── config/ │ ├── 1280x720/ │ │ └── config.json │ ├── 1920x1080/ │ │ └── config.json │ └── default.json ├── douyin-bot.py ├── example/ │ ├── test_crop.py │ ├── test_plot.py │ ├── test_readExcel.py │ └── test_textInput.py ├── reply/ │ └── data.json └── requirements.txt
SYMBOL INDEX (39 symbols across 10 files)
FILE: common/UnicodeStreamFilter.py
class UnicodeStreamFilter (line 5) | class UnicodeStreamFilter:
method __init__ (line 7) | def __init__(self, target):
function write (line 14) | def write(self, s):
FILE: common/apiutil.py
function setParams (line 13) | def setParams(array, key, value):
function genSignString (line 17) | def genSignString(parser):
class AiPlat (line 29) | class AiPlat(object):
method __init__ (line 30) | def __init__(self, app_id, app_key):
method invoke (line 36) | def invoke(self, params):
method face_detectface (line 48) | def face_detectface(self, image, mode):
FILE: common/auto_adb.py
class auto_adb (line 7) | class auto_adb():
method __init__ (line 8) | def __init__(self):
method get_screen (line 32) | def get_screen(self):
method run (line 37) | def run(self, raw_command):
method test_device (line 44) | def test_device(self):
method test_density (line 60) | def test_density(self):
method test_device_detail (line 65) | def test_device_detail(self):
method test_device_os (line 70) | def test_device_os(self):
method adb_path (line 75) | def adb_path(self):
FILE: common/compression.py
function resize_image (line 6) | def resize_image(origin_img, optimize_img, threshold):
FILE: common/config.py
function open_accordant_config (line 15) | def open_accordant_config():
function _get_screen_size (line 45) | def _get_screen_size():
FILE: common/debug.py
function make_debug_dir (line 29) | def make_debug_dir(screenshot_backup_dir):
function backup_screenshot (line 37) | def backup_screenshot(ts):
function save_debug_screenshot (line 47) | def save_debug_screenshot(ts, im, piece_x, piece_y, board_x, board_y):
function computing_error (line 66) | def computing_error(last_press_time, target_board_x, target_board_y, las...
function dump_device_info (line 83) | def dump_device_info():
FILE: common/excel_keyword.py
function get_random_keyword (line 5) | def get_random_keyword(filename):
FILE: common/screenshot.py
function pull_screenshot (line 23) | def pull_screenshot():
function check_screenshot (line 45) | def check_screenshot():
FILE: douyin-bot.py
function yes_or_no (line 43) | def yes_or_no():
function _random_bias (line 59) | def _random_bias(num):
function next_page (line 68) | def next_page():
function follow_user (line 84) | def follow_user():
function thumbs_up (line 97) | def thumbs_up():
function tap (line 110) | def tap(x, y):
function auto_reply (line 118) | def auto_reply():
function parser (line 141) | def parser():
function main (line 149) | def main():
FILE: example/test_textInput.py
function adb_keyboard_input (line 4) | def adb_keyboard_input(text):
Condensed preview — 25 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (28K chars).
[
{
"path": ".gitignore",
"chars": 1253,
"preview": ".idea/\n.DS_Store\n# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distr"
},
{
"path": "LICENSE",
"chars": 1065,
"preview": "MIT License\n\nCopyright (c) 2018 神奇的战士-王松\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\no"
},
{
"path": "README.md",
"chars": 1803,
"preview": "# 如何在抖音上找到漂亮小姐姐----抖音机器人\n\n[](https://github."
},
{
"path": "Tools/README.md",
"chars": 356,
"preview": "## 所有实验都在该文件夹下运行即可,已放上测试用的代码。免去配置 adb 的麻烦\n\n- 复制 wechat_jump_game 根目录下的 config 文件夹以及 wechat_jump_py3.py 文件到本目录下\n- 按住 `shi"
},
{
"path": "common/UnicodeStreamFilter.py",
"chars": 589,
"preview": "# -*- coding: utf-8 -*-\nimport sys\n\nif sys.version_info.major != 3:\n class UnicodeStreamFilter:\n\n def __init__"
},
{
"path": "common/__init__.py",
"chars": 0,
"preview": ""
},
{
"path": "common/apiutil.py",
"chars": 1876,
"preview": "#-*- coding: UTF-8 -*-\r\nimport hashlib\r\nimport urllib\r\nfrom urllib import parse\r\nimport urllib.request\r\nimport base64\r\ni"
},
{
"path": "common/auto_adb.py",
"chars": 2443,
"preview": "# -*- coding: utf-8 -*-\nimport os\nimport subprocess\nimport platform\n\n\nclass auto_adb():\n def __init__(self):\n "
},
{
"path": "common/compression.py",
"chars": 833,
"preview": "from PIL import Image\nimport math\nimport os\n\n\ndef resize_image(origin_img, optimize_img, threshold):\n \"\"\"\n shrink "
},
{
"path": "common/config.py",
"chars": 1277,
"preview": "# -*- coding: utf-8 -*-\n\"\"\"\n调取配置文件和屏幕分辨率的代码\n\"\"\"\nimport os\nimport sys\nimport json\nimport re\n\nfrom common.auto_adb import "
},
{
"path": "common/debug.py",
"chars": 3349,
"preview": "# -*- coding: utf-8 -*-\n\"\"\"\n这是debug的代码,当DEBUG_SWITCH开关开启的时候,会将各种信息存在本地,方便检查故障\n\"\"\"\nimport os\nimport sys\nimport shutil\nimp"
},
{
"path": "common/excel_keyword.py",
"chars": 561,
"preview": "import xlrd\nimport random\n\n\ndef get_random_keyword(filename):\n \"\"\"\n get random row of filename\n :param filename"
},
{
"path": "common/screenshot.py",
"chars": 1677,
"preview": "# -*- coding: utf-8 -*-\n\"\"\"\n手机屏幕截图的代码\n\"\"\"\nimport subprocess\nimport os\nimport sys\nfrom PIL import Image\nfrom io import St"
},
{
"path": "config/1280x720/config.json",
"chars": 242,
"preview": "{\n \"center_point\":{\n \"x\": 333,\n \"y\": 700,\n \"rx\": 10,\n \"ry\": 300\n },\n \"follow_bottom\":{\n \"x\": 648,\n "
},
{
"path": "config/1920x1080/config.json",
"chars": 489,
"preview": "{\n \"center_point\":{\n \"x\": 540,\n \"y\": 965,\n \"rx\": 10,\n \"ry\": 300\n },\n \"follow_bottom\":{\n \"x\": 983,\n "
},
{
"path": "config/default.json",
"chars": 491,
"preview": "{\n \"center_point\":{\n \"x\": 540,\n \"y\": 965,\n \"rx\": 10,\n \"ry\": 300\n },\n \"follow_bottom\":{\n \"x\": 990,\n "
},
{
"path": "douyin-bot.py",
"chars": 5374,
"preview": "# -*- coding: utf-8 -*-\nimport sys\nimport random\nimport time\nfrom PIL import Image\nimport argparse\n\nif sys.version_info."
},
{
"path": "example/test_crop.py",
"chars": 141,
"preview": "from PIL import Image\n\nim = Image.open(\"../autojump.png\")\nw, h = im.size\n\narea = (0, 0, 50, 50)\n\nim_croped = im.crop(are"
},
{
"path": "example/test_plot.py",
"chars": 492,
"preview": "import matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\nimport numpy as np\n\n# img = mpimg.imread('../screenshot"
},
{
"path": "example/test_readExcel.py",
"chars": 281,
"preview": "import xlrd\nimport random\n#打开excel文件\ndata=xlrd.open_workbook('../reply/keyword.xlsx')\n#获取第一张工作表(通过索引的方式)\ntable=data.shee"
},
{
"path": "example/test_textInput.py",
"chars": 288,
"preview": "import os\n\n\ndef adb_keyboard_input(text):\n \"\"\"\n adb keyboard app input unicode text\n :param text:\n :return:\n"
},
{
"path": "reply/data.json",
"chars": 37,
"preview": "{\n \"data\":[\n \"你好,陌生人\"\n ]\n}"
},
{
"path": "requirements.txt",
"chars": 93,
"preview": "matplotlib==2.2.0\nxlrd==1.1.0\npandas==0.22.0\nnumpy==1.14.1\nPillow==5.1.0\nscikit-learn==0.19.1"
}
]
// ... and 2 more files (download for full content)
About this extraction
This page contains the full source code of the wangshub/Douyin-Bot GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 25 files (24.4 KB), approximately 7.9k tokens, and a symbol index with 39 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.