├── .gitignore
├── .idea
├── .gitignore
├── AntifraudBOT.iml
├── compiler.xml
├── jarRepositories.xml
├── misc.xml
├── modules.xml
└── vcs.xml
├── LICENSE
├── README.md
├── __init__.py
├── crawler
├── README.MD
├── database
│ └── sql_executor.py
├── test.py
├── utils
│ ├── add_database.py
│ ├── auto_utils.py
│ ├── downloader_manager.py
│ ├── findauthor.py
│ ├── get_reply.py
│ ├── get_video_html.py
│ ├── logger.py
│ ├── requester.py
│ └── seg_downloader.py
└── worker.py
├── dialog.py
├── ding-dong-bot.py
├── inspurai.py
├── log
├── 11_11_2022.txt
├── 12_10_2022.txt
├── 15_11_2022.txt
├── 16_11_2022.txt
└── 30_09_2022.txt
├── memory
├── __init__.py
├── forget.py
└── longshort.py
├── prompt
├── bot_example.txt
├── bot_info.txt
├── scripts
└── users.json
├── python-wechaty
├── .codecov.yml
├── .editorconfig
├── .gitignore
├── .pre-commit-config.yaml
├── .pylintrc
├── .readthedocs.yaml
├── .travis.yml
├── LICENSE
├── MAINTAINERS
├── MANIFEST.in
├── Makefile
├── NOTICE
├── README.md
├── VERSION
├── docs
│ ├── api
│ │ ├── accessory.md
│ │ ├── config.md
│ │ ├── plugin.md
│ │ ├── types.md
│ │ ├── user
│ │ │ ├── contact.md
│ │ │ ├── contact_self.md
│ │ │ ├── favorite.md
│ │ │ ├── friendship.md
│ │ │ ├── image.md
│ │ │ ├── message.md
│ │ │ ├── mini_program.md
│ │ │ ├── room.md
│ │ │ ├── room_invitation.md
│ │ │ ├── tag.md
│ │ │ └── url_link.md
│ │ ├── utils
│ │ │ ├── async_helper.md
│ │ │ ├── date_util.md
│ │ │ ├── link.md
│ │ │ ├── qr_code.md
│ │ │ ├── qrcode_terminal.md
│ │ │ └── type_check.md
│ │ └── wechaty.md
│ ├── explanation
│ │ ├── different_protocol.md
│ │ ├── index.md
│ │ └── why_plugin.md
│ ├── faq
│ │ ├── common.md
│ │ ├── faq.md
│ │ └── what-is-a-puppet.md
│ ├── how-to-contribute-for-docs.md
│ ├── how-to
│ │ ├── how-to_add_friendship.md
│ │ ├── how-to_auto_reply.md
│ │ ├── how-to_finder.md
│ │ ├── how-to_github_webhook.md
│ │ ├── how-to_gitlab_webhook.md
│ │ ├── how-to_introduction.md
│ │ ├── how-to_message_forward.md
│ │ ├── how-to_rasa.md
│ │ ├── how-to_room_inviter.md
│ │ ├── how-to_scheduler.md
│ │ ├── how-to_use_plugin.md
│ │ ├── use-padlocal-protocol.md
│ │ └── use-web-protocol.md
│ ├── img
│ │ ├── favicon.ico
│ │ ├── getting-started
│ │ │ └── python-wechaty.png
│ │ ├── introduction
│ │ │ └── cloud.png
│ │ ├── wechaty-icon-white.svg
│ │ └── wechaty-logo.svg
│ ├── index.md
│ ├── introduction.md
│ ├── introduction
│ │ ├── index.md
│ │ ├── use-padlocal-protocol.md
│ │ ├── use-paimon-protocol.md
│ │ └── use-web-protocol.md
│ ├── references
│ │ ├── contact-self.md
│ │ ├── contact.md
│ │ ├── filebox.md
│ │ ├── friendship.md
│ │ ├── index.md
│ │ ├── message.md
│ │ ├── room-invitation.md
│ │ ├── room.md
│ │ └── wechaty.md
│ └── tutorials
│ │ ├── getting-started.md
│ │ ├── index.md
│ │ ├── use_padlocal_getting_started.md
│ │ ├── use_paimon_getting_started.md
│ │ ├── use_web_getting_started.md
│ │ └── videos.md
├── examples
│ ├── contact-bot.py
│ ├── ding-dong-bot-oop.py
│ ├── ding-dong-bot.py
│ ├── health_check_plugin.py
│ └── plugin-server-bot.py
├── mkdocs.yml
├── pyproject.toml
├── requirements-dev.txt
├── requirements.txt
├── scripts
│ └── check_python_version.py
├── setup.cfg
├── setup.py
├── src
│ └── wechaty
│ │ ├── __init__.py
│ │ ├── accessory.py
│ │ ├── config.py
│ │ ├── exceptions.py
│ │ ├── plugin.py
│ │ ├── py.typed
│ │ ├── types.py
│ │ ├── user
│ │ ├── __init__.py
│ │ ├── contact.py
│ │ ├── contact_self.py
│ │ ├── favorite.py
│ │ ├── friendship.py
│ │ ├── image.py
│ │ ├── message.py
│ │ ├── message.pyi
│ │ ├── mini_program.py
│ │ ├── room.py
│ │ ├── room.pyi
│ │ ├── room_invitation.py
│ │ ├── tag.py
│ │ └── url_link.py
│ │ ├── utils
│ │ ├── __init__.py
│ │ ├── async_helper.py
│ │ ├── date_util.py
│ │ ├── link.py
│ │ ├── qr_code.py
│ │ ├── qrcode_terminal.py
│ │ └── type_check.py
│ │ ├── version.py
│ │ └── wechaty.py
├── tests
│ ├── accessory_test.py
│ ├── config_test.py
│ ├── conftest.py
│ ├── room_test.py
│ ├── smoke_testing_test.py
│ ├── timestamp_test.py
│ ├── url_link_test.py
│ ├── user_message_test.py
│ ├── utils_test.py
│ ├── version_test.py
│ └── wechaty_test.py
└── wip
│ └── wechaty
│ └── __init__.py
├── rasa_chatbot_cn
├── .gitignore
├── Makefile
├── __init__.py
├── actions
│ └── actions.py
├── config.yml
├── configs
│ └── endpoints.yml
├── data
│ ├── core
│ │ └── stories.md
│ └── nlu
│ │ └── nlu.json
├── dev
│ ├── Dockerfile.dev
│ ├── deploy_dev.sh
│ └── run.sh
├── domain.yml
└── process.py
├── rooms.json
├── script
├── 源1.0预训练语言模型使用示例V1.pdf
└── 源APIExp使用手册0510.pdf
├── test.py
└── url_config.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 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/AntifraudBOT.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/jarRepositories.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/__init__.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import os
3 |
4 | sys.path.append(os.path.abspath(os.path.dirname(__file__)))
--------------------------------------------------------------------------------
/crawler/README.MD:
--------------------------------------------------------------------------------
1 | Green类可以爬取一个up的所有动态信息、相册、收藏夹、视频索引、关注与被关注信息,并添加到指定的数据库中,再使用该类中的run方法,便可以遍历爬取所有视频本体和附加信息;
2 | Blue类是一个仅用于不断爬取用户数据和用户关系的类,可以爬取每个用户的动态信息、相册、收藏夹、视频索引信息,并能够在关注列表中发现新的用户,进而建立起一个庞大的用户信息和用户关系网络,可以制作数据集、推荐系统以及搞科研用。
3 |
4 |
5 | ## 准备工作
6 | ### pip依赖库
7 | requests
8 | tqdm
9 | ### 系统依赖插件
10 | ffmpeg (需添加至环境变量,macos、linux使用brew、apt、yum等工具均可直接安装,windows下安装方法自行百度)
11 |
12 |
13 | ## 使用方法
14 | 这里只讲强大的Green类
15 |
16 | 看到worker.py,实例化一个Green类,传入数据库路径(没有数据库的话会自动创建)、up的视频路径、up的相册的路径(没有皆会自动创建),即可初始化完成。
17 |
18 | 调用find_ups([up主mid, ])即可获取一个up主的态、相册、收藏夹、视频索引、关注与被关注信息,并添加到sqlite数据库中。
19 |
20 | 在调用run()方法,即可按照视频索引信息按顺序爬取up主的视频与附加信息到指定目录中。
21 |
22 | 详细用例可以在文件中的main函数中找到。
23 |
24 | Enjoy it~
25 |
26 |
27 |
28 | ## TODO
29 | 当系统封禁ip时自动切换内网ip地址。
30 |
31 |
32 |
--------------------------------------------------------------------------------
/crawler/database/sql_executor.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 |
3 | class SQLHandler(object):
4 | def __init__(self, path):
5 | self.db = sqlite3.connect(path)
6 | self.cursor = self.db.cursor()
7 |
8 | def execute(self, sql):
9 | try:
10 | # print(sql)
11 | self.cursor.execute(sql)
12 | self.db.commit()
13 | except sqlite3.IntegrityError:
14 | pass
15 |
16 | def execute_return(self, sql):
17 | ret = self.cursor.execute(sql)
18 | return ret
19 |
20 | def insert_binary(self, sql, bin_obj, ):
21 | # print(sql)
22 | self.cursor.execute(sql, (bin_obj, ))
23 | self.db.commit()
24 |
25 | def close(self):
26 | self.db.close()
--------------------------------------------------------------------------------
/crawler/test.py:
--------------------------------------------------------------------------------
1 | import json
2 | import requests
3 | from decimal import Decimal
4 | import pprint
5 | headers ={
6 | 'referer': 'https://www.bilibili.com/video/BV1t3411p7Vq?spm_id_from=444.41.0.0',
7 | 'user-agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.69 Safari/537.36 Edg/95.0.1020.53',
8 | }
9 | main_url = 'https://api.bilibili.com/x/v2/reply/main?'
10 | main_params = {
11 | 'jsonp': 'jsonp',
12 | 'next': 1,
13 | 'type': '1',
14 | 'oid': '425009881',
15 | 'mode': '3',
16 | 'plat': '1',
17 | '_': '1648289436898'
18 | }
19 | reply_url = 'https://api.bilibili.com/x/v2/reply/reply'
20 | reply_params = {
21 | 'jsonp': 'jsonp',
22 | 'pn': '1',
23 | 'type': '1',
24 | 'oid': '425009881',
25 | 'ps': '100',
26 | 'root': '106841868432',
27 | '_': '1648290346968'
28 | }
29 | proxies={
30 | 'http':'20.24.65.59:6655'
31 | }
32 | dic = {}
33 | dic['count'] = Decimal('0')
34 | #楼中楼
35 | def parse_reply(root,ps,count):
36 | reply_params['root'] = root
37 | reply_params['ps'] = ps
38 | response = requests.get(url=reply_url, headers=headers, params=reply_params)
39 | page_text_json = response.json()
40 | for comment in page_text_json['data']['replies']:
41 | dic['count'] += Decimal('0.0001')
42 | dic['username'] = comment['member']['uname']
43 | dic['content'] = comment['content']['message']
44 | dic['like_count'] = comment['like']
45 | print(dic)
46 | f.write(str(dic['count']) + str(dic['username']) + str(dic['content']) + str(dic['like_count']) + '\n')
47 | dic['count'] = count
48 | #评论楼层
49 | def parse_main_reply():
50 | response = requests.get(url = main_url, headers = headers,params = main_params)
51 | page_text = response.text
52 | page_text_json = response.json()
53 | with open('test.html','w',encoding='utf-8') as fp:
54 | fp.write(page_text)
55 | fp.close()
56 | if page_text_json['data']['cursor']['is_end'] == False:
57 | for comment in page_text_json['data']['replies']:
58 | dic['count'] += Decimal('1')
59 | dic['username'] = comment['member']['uname']
60 | dic['content'] = comment['content']['message']
61 | dic['reply_count'] = comment['rcount']
62 | dic['reply_id'] = comment['rpid']
63 | dic['like_count'] = comment['like']
64 | dic['total_reply_num'] = page_text_json['data']['cursor']['all_count']
65 | dic['is_end'] = page_text_json['data']['cursor']['is_end']
66 | print(dic)
67 | f.write(str(dic['count'])+str(dic['username'])+str(dic['content'])+str(dic['reply_count'])+str(dic['reply_id'])+str(dic['like_count'])+str(dic['total_reply_num'])+str(dic['is_end'])+'\n')
68 | if dic['reply_count'] != 0:
69 | parse_reply(dic['reply_id'],dic['reply_count'],dic['count'])
70 | main_params['next'] += 1
71 | return parse_main_reply()
72 | elif page_text_json['data']['cursor']['is_end'] == True:
73 | print('结束')
74 | f = open('test.txt','a',encoding='utf-8')
75 | parse_main_reply()
76 | f.close()
--------------------------------------------------------------------------------
/crawler/utils/auto_utils.py:
--------------------------------------------------------------------------------
1 | from utils.add_database import DBProxy
2 |
3 | class AutoUtils:
4 | def __init__(self, db_path):
5 | self.db_path = db_path
6 | self.db_proxy = DBProxy(db_path)
7 | def get_one_user(self):
8 | ret = self.db_proxy.select_one_author()
9 | return ret[0]
10 |
11 | def flag_one_video(self, bvid):
12 | self.db_proxy.flag_one_video(bvid)
13 | pass
14 |
15 | def get_one_vid(self,):
16 | return self.db_proxy.select_one_video()[0]
17 |
18 | def flag_one_err_author(self, mid):
19 | return self.db_proxy.flag_one_author_error(mid)
20 |
21 |
22 | def flag_one_user_done(self, mid):
23 | self.db_proxy.flag_one_author(mid)
24 |
25 | if __name__ == '__main__':
26 |
27 | autoutils = AutoUtils('test2.db')
28 | # autoutils.get_one_user()
29 | print(autoutils.get_one_vid())
30 | # TODO:增加断点续传功能
--------------------------------------------------------------------------------
/crawler/utils/get_reply.py:
--------------------------------------------------------------------------------
1 | # coding:utf8
2 | import requests
3 |
4 |
5 | def get_raw_reply(aid):
6 | headers = {
7 | 'accept': '*/*',
8 | # 'accept-encoding': 'gzip, deflate, br',
9 | 'accept-encoding': 'deflate',
10 | 'accept-language': 'zh-CN,zh;q=0.9',
11 | 'cache-control': 'no-cache',
12 | # 'cookie': 'pgv_pvi=300764160; rpdid=olwwwliplodosoqqwokww; _uuid=EA9827E7-550F-BFB8-CA1B-8257DCDC960468224infoc; buvid3=93A293B0-2EAC-4F96-87BB-E7D398CBCC57148797infoc; CURRENT_FNVAL=80; blackside_state=1; fingerprint=bc56ed3fa67484bb54a6f2c0d34d101f; buvid_fp=93A293B0-2EAC-4F96-87BB-E7D398CBCC57148797infoc; buvid_fp_plain=93A293B0-2EAC-4F96-87BB-E7D398CBCC57148797infoc; SESSDATA=d82262f7%2C1642232589%2Cee061%2A71; bili_jct=bfba5357d0ca1966160cce82a70380b7; DedeUserID=23064683; DedeUserID__ckMd5=864e26ed3b2a0940; sid=56yroqa6; CURRENT_QUALITY=80; PVID=1; bfe_id=5db70a86bd1cbe8a88817507134f7bb5',
13 | 'pragma': 'no-cache',
14 | # 'referer': 'https://www.bilibili.com/video/BV1XL411H79r',
15 | 'sec-ch-ua': '" Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"',
16 | 'sec-ch-ua-mobile': '?0',
17 | 'sec-fetch-dest': 'script',
18 | 'sec-fetch-mode': 'no-cors',
19 | 'sec-fetch-site': 'same-site',
20 | 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36'
21 | }
22 | page = 0
23 | reply_lst = []
24 | while True:
25 | assert aid.isdigit(), 'aid is not digit'
26 | url = 'https://api.bilibili.com/x/v2/reply/main?jsonp=jsonp&next={}&type=1&oid={}&mode=3&plat=1&'.format(page, aid)
27 | # url = 'https://api.bilibili.com/x/v2/reply/main?callback=jQuery17207723215742327245_1627299231613&jsonp=jsonp&next=7&type=1&oid=461774991&mode=3&plat=1&_=1627299537026'
28 | req = requests.get(url, headers=headers)
29 | req.encoding = req.apparent_encoding
30 | # print(req.content.decode('utf8'))
31 | content = req.content.decode('utf8')
32 | json = content.replace('null', 'None').replace('true', 'True').replace('false', 'False')
33 | json = eval(json)
34 | # print(json)
35 | if json["data"]['cursor']['is_end']:
36 | break
37 | reply_lst.append(json)
38 | page += 1
39 |
40 | # print(reply_lst)
41 | return reply_lst
42 |
43 |
44 | if __name__ == '__main__':
45 |
46 | # print(get_raw_reply('461774991'))
47 | # print(get_raw_reply('461774991'))
48 | print(get_raw_reply('360863614'))
49 |
50 |
--------------------------------------------------------------------------------
/crawler/utils/logger.py:
--------------------------------------------------------------------------------
1 | import time
2 | import pandas as pd
3 |
4 | def logger(info, type='INFO'):
5 | print('[{}]{}: {}'.format(time.strftime("%Y-%m-%d %H:%M:%S"),type, info))
6 |
7 | def err_logger(err_msg, _id, err_code=0, err_log_path='err.log'):
8 | """
9 | 0: following err
10 | 1: danmaku err
11 | 2: follower err
12 | 3:
13 |
14 | :param err_msg:
15 | :param _id:
16 | :param status_code:
17 | :param err_log_path:
18 | :return:
19 | """
20 | df = pd.DataFrame([[err_msg, _id, err_code], ])
21 | df.to_csv(err_log_path, mode='a', header=None)
22 |
23 |
24 | if __name__ == '__main__':
25 | pass
26 | # logger('qweqwe')
27 | # err_logger('123', '34')
--------------------------------------------------------------------------------
/crawler/utils/requester.py:
--------------------------------------------------------------------------------
1 | import requests
2 |
3 | def request(url, headers=None):
4 | if not headers:
5 | headers = {'user-agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36'}
6 | return requests.get(url, headers=headers).content
--------------------------------------------------------------------------------
/crawler/utils/seg_downloader.py:
--------------------------------------------------------------------------------
1 | from utils.requester import request
2 | def download_segment(url):
3 | headers = {
4 | 'accept': '*/*',
5 | 'accept-encoding': 'identity',
6 | 'accept-language': 'zh-CN,zh;q=0.9',
7 | 'if-range': 'Thu, 22 Jul 2021 09:43:08 GMT',
8 | 'origin': 'https://www.bilibili.com',
9 | 'range': 'bytes=0-1995353',
10 | 'referer': 'https://www.bilibili.com/video/BV1XL411H79r',
11 | 'sec-ch-ua': '" Not;A Brand";v="99", "Google Chrome";v="91", "Chromium";v="91"',
12 | 'sec-ch-ua-mobile': '?0',
13 | 'sec-fetch-dest': 'empty',
14 | 'sec-fetch-mode': 'cors',
15 | 'sec-fetch-site': 'cross-site',
16 | 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.164 Safari/537.36'
17 | }
18 | # url='https://xy121x31x142x181xy.mcdn.bilivideo.cn:4483/upgcxcode/56/25/374632556/374632556_nb2-1-30280.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEqxTEto8BTrNvN0GvT90W5JZMkX_YN0MvXg8gNEV4NC8xNEV4N03eN0B5tZlqNxTEto8BTrNvNeZVuJ10Kj_g2UB02J0mN0B5tZlqNCNEto8BTrNvNC7MTX502C8f2jmMQJ6mqF2fka1mqx6gqj0eN0B599M=&uipk=5&nbs=1&deadline=1626969237&gen=playurlv2&os=mcdn&oi=1885373441&trid=000191f5b7d25fed4e63bbd80e017001bc64u&platform=pc&upsig=e74d310419272cfdb787edfcf3a9434c&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform&mcdnid=9000626&mid=23064683&bvc=vod&nettype=0&orderid=0,3&agrr=0&logo=A0000100'
19 |
20 | # url2 = 'https://xy171x39x15x196xy.mcdn.bilivideo.cn:4483/upgcxcode/56/25/374632556/374632556_nb2-1-30080.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEqxTEto8BTrNvN0GvT90W5JZMkX_YN0MvXg8gNEV4NC8xNEV4N03eN0B5tZlqNxTEto8BTrNvNeZVuJ10Kj_g2UB02J0mN0B5tZlqNCNEto8BTrNvNC7MTX502C8f2jmMQJ6mqF2fka1mqx6gqj0eN0B599M=&uipk=5&nbs=1&deadline=1627038912&gen=playurlv2&os=mcdn&oi=1885389723&trid=0001d1522260ac414b16bf9a5541b8578eb0u&platform=pc&upsig=356be02417e4af1928ec3d264bb8f943&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform&mcdnid=9000723&mid=28697381&bvc=vod&nettype=0&orderid=0,3&agrr=1&logo=A000010'
21 |
22 | content = request(url, headers=headers)
23 | return content
24 |
25 |
26 |
27 |
28 |
29 | # quit()
30 | # with open('test.m4s', 'wb') as f:
31 | # f.write(c)
32 | # f.close()
33 |
34 |
--------------------------------------------------------------------------------
/crawler/worker.py:
--------------------------------------------------------------------------------
1 | from utils.logger import logger, err_logger
2 | from utils.auto_utils import AutoUtils
3 | from utils.findauthor import run
4 | from utils.downloader_manager import executor, single_video
5 |
6 | import os
7 | import argparse
8 | from tqdm import tqdm
9 |
10 | class GreenSystem:
11 | __doc__ = """
12 | 用于下载B站视频or评论,从命令行获取要被爬取的up(可以遍历也可以指定)
13 | 启动后,将不停地爬取数据库中的视频
14 | """
15 |
16 | def __init__(self, db_path, user_dy_path, video_down_path):
17 | self.db_path = db_path
18 | self.user_dy_path = user_dy_path
19 | self.video_down_path = video_down_path
20 | self.check_dirs()
21 | self.findauthor = run
22 | self.auto_utils = AutoUtils(db_path=self.db_path)
23 |
24 | @staticmethod
25 | def make_dirs(path):
26 | if not os.path.exists(path):
27 | os.makedirs(path, exist_ok=True)
28 |
29 | def check_dirs(self):
30 | self.make_dirs(self.user_dy_path)
31 | self.make_dirs(self.video_down_path)
32 |
33 | def run(self):
34 | while True:
35 | bvid = self.auto_utils.get_one_vid()
36 | # executor([bvid,], location=self.video_down_path)
37 | single_video(bvid, location=self.video_down_path)
38 | self.auto_utils.flag_one_video(bvid)
39 |
40 |
41 | def find_ups(self, ups=[]):
42 | for mid in tqdm(ups):
43 | self.findauthor(mid, down_pics=True, down_loc=self.user_dy_path, db_path=self.db_path)
44 |
45 |
46 | class BlueSystem(object):
47 | __doc__ = """
48 | 不获取视频和图片,专注于研究单个用户数据和用户关系
49 | """
50 |
51 | def __init__(self, db_path):
52 | self.db_path = db_path
53 | self.findauthor = run
54 | self.auto_utils = AutoUtils(db_path=self.db_path)
55 |
56 | def get_seed(self, ups=[]):
57 | for mid in tqdm(ups):
58 | self.findauthor(mid, down_pics=False, db_path=self.db_path, down_loc=None,
59 | get_img=False, get_collect=False, get_update=True, vid_limit=20)
60 |
61 | def run(self):
62 | while True:
63 | mid = self.auto_utils.get_one_user()
64 | # try:
65 | self.findauthor(mid, down_pics=False, db_path=self.db_path, down_loc=None, get_img=False, get_collect=False, get_update=True, vid_limit=20)
66 | # except:
67 | # self.auto_utils.flag_one_err_author(mid)
68 |
69 |
70 | def main():
71 | argument = argparse.ArgumentParser()
72 | argument.add_argument('-s', '--seed-mid', default='``NULL``', type=str, help="Target user's mid (required).")
73 | argument.add_argument('-d', '--db', default='GREEN.DB', type=str, help='Database path.')
74 | argument.add_argument('-ud', '--user-dynamics', default='data/green/user_dynamics', type=str, help='User dynamic photos path.')
75 | argument.add_argument('-vp', '--video-path', default='data/green/videos', type=str, help='User Videos path.')
76 | argument.add_argument('-r', '--run', action='store_true', help='Enable Endless Running Mode. ')
77 |
78 | args = argument.parse_args()
79 |
80 | green_system = GreenSystem(db_path=args.db, user_dy_path=args.user_dynamics, video_down_path=args.video_path)
81 | if args.seed_mid != '```NULL```':
82 | green_system.find_ups([args.mid, ])
83 | if args.run:
84 | green_system.run()
85 |
86 |
87 | def main_blue():
88 | blue_system = BlueSystem(db_path='BLUE.DB')
89 | # blue_system.get_seed(['13074237', ])
90 | blue_system.run()
91 |
92 |
93 | if __name__ == '__main__':
94 | main_blue()
--------------------------------------------------------------------------------
/ding-dong-bot.py:
--------------------------------------------------------------------------------
1 | """
2 | Python Wechaty - https://github.com/wechaty/python-wechaty
3 | Authors: Huan LI (李卓桓)
4 | Jingjing WU (吴京京)
5 | 2020 @ Copyright Wechaty Contributors
6 | Licensed under the Apache License, Version 2.0 (the 'License');
7 | you may not use this file except in compliance with the License.
8 |
9 | You may obtain a copy of the License athttp://www.apache.org/licenses/LICENSE-2.0
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an 'AS IS' BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and limitations under the License.
14 | """
15 | import os
16 | import asyncio
17 | import subprocess
18 |
19 | from urllib.parse import quote
20 |
21 | from wechaty import (
22 | Contact,
23 | FileBox,
24 | Message,
25 | Wechaty,
26 | ScanStatus,
27 | )
28 |
29 |
30 | async def on_message(msg: Message):
31 | """
32 | Message Handler for the Bot
33 | """
34 | if msg.text() == 'ding':
35 | await msg.say('dong')
36 |
37 | file_box = FileBox.from_url(
38 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/'
39 | 'u=1116676390,2305043183&fm=26&gp=0.jpg',
40 | name='ding-dong.jpg'
41 | )
42 | try:
43 | await msg.say(file_box)
44 | except Exception as e:
45 | print("catch some errors:", e)
46 |
47 |
48 | async def on_scan(
49 | qrcode: str,
50 | status: ScanStatus,
51 | _data,
52 | ):
53 | """
54 | Scan Handler for the Bot
55 | """
56 | print('Status: ' + str(status))
57 | print('View QR Code Online: https://wechaty.js.org/qrcode/' + quote(qrcode))
58 |
59 |
60 | async def on_login(user: Contact):
61 | """
62 | Login Handler for the Bot
63 | """
64 | print(user)
65 | # TODO: To be written
66 |
67 |
68 | async def main():
69 | """
70 | Async Main Entry
71 | """
72 | #
73 | # Make sure we have set WECHATY_PUPPET_SERVICE_TOKEN in the environment variables.
74 | # Learn more about services (and TOKEN) from https://wechaty.js.org/docs/puppet-services/
75 | #
76 | # It is highly recommanded to use token like [paimon] and [wxwork].
77 | # Those types of puppet_service are supported natively.
78 | # https://wechaty.js.org/docs/puppet-services/paimon
79 | # https://wechaty.js.org/docs/puppet-services/wxwork
80 | #
81 | # Replace your token here and umcommt that line, you can just run this python file successfully!
82 | # os.environ['token'] = 'puppet_paimon_your_token'
83 | # os.environ['token'] = 'puppet_wxwork_your_token'
84 | #
85 | if 'WECHATY_PUPPET_SERVICE_TOKEN' not in os.environ:
86 | print('''
87 | Error: WECHATY_PUPPET_SERVICE_TOKEN is not found in the environment variables
88 | You need a TOKEN to run the Python Wechaty. Please goto our README for details
89 | https://github.com/wechaty/python-wechaty-getting-started/#wechaty_puppet_service_token
90 | ''')
91 |
92 | #for cloud-service
93 | os.environ['WECHATY_PUPPET_SERVICE_TOKEN']='puppet_padlocal_3f904c6aa9cd4c548944f6688a829707'
94 | os.environ['WECHATY_PUPPET']='wechaty-puppet-padlocal'
95 | os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT']='43.154.97.162:8788'
96 |
97 | # export WECHATY_PUPPET=wechaty-puppet-padlocal
98 | # export WECHATY_PUPPET_PADLOCAL_TOKEN=puppet_padlocal_3f904c6aa9cd4c548944f6688a829707
99 | # export WECHATY_PUPPET_SERVER_PORT=8788
100 | # export WECHATY_LOG=verbose
101 |
102 | #for local-service
103 | # os.environ['WECHATY_PUPPET_SERVICE_TOKEN']='test01'
104 | # os.environ['WECHATY_PUPPET']='wechaty-puppet-xp'
105 | # os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT']='127.0.0.1:8080'
106 |
107 | # os.environ['WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER']='true'
108 | # os.environ['WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_CLIENT']='true'
109 |
110 | bot = Wechaty()
111 |
112 | bot.on('scan', on_scan)
113 | bot.on('login', on_login)
114 | bot.on('message', on_message)
115 |
116 | await bot.start()
117 |
118 | print('[Python Wechaty] Ding Dong Bot started.')
119 |
120 |
121 | asyncio.run(main())
--------------------------------------------------------------------------------
/log/11_11_2022.txt:
--------------------------------------------------------------------------------
1 | Q:是啊---top-3 similarity:0.9071,0.8457,0.8092
2 | 你是?
3 | Q:我是北京土著---top-3 similarity:0.7117,0.625,0.6184
4 | 我是上海土著
5 | Q:你呢,你是哪里人---top-3 similarity:0.9205,0.8155,0.7798
6 | 我是上海人
7 | Q:对,我是北京人---top-3 similarity:0.7934,0.7392,0.7199
8 | 你是?
9 | Q:我是一只猫---top-3 similarity:0.8468,0.8343,0.8269
10 | 你是?
11 | Q:你爹---top-3 similarity:0.7873,0.7304,0.7226
12 | 你是?
13 |
--------------------------------------------------------------------------------
/log/12_10_2022.txt:
--------------------------------------------------------------------------------
1 | Q:你叫什么呀---top-3 similarity:0.909,0.8278,0.8135
2 | 我叫水思源。
3 |
--------------------------------------------------------------------------------
/log/15_11_2022.txt:
--------------------------------------------------------------------------------
1 | Q:嗨---top-3 similarity:0.7775,0.7657,0.7615
2 | 你好
3 |
--------------------------------------------------------------------------------
/log/16_11_2022.txt:
--------------------------------------------------------------------------------
1 | Q:小源你好---top-3 similarity:0.5962,0.5455,0.5256
2 | 你好
3 | Q:起床了吗---top-3 similarity:0.7902,0.7515,0.712
4 | 还没呢
5 | Q:嘿---top-3 similarity:0.7961,0.7857,0.7794
6 | 你好
7 |
--------------------------------------------------------------------------------
/log/30_09_2022.txt:
--------------------------------------------------------------------------------
1 | Q:你叫什么名字---top-3 similarity:0.8545,0.7954,0.7227
2 | 我叫水思源
3 | Q:哇真好听---top-3 similarity:0.7374,0.6422,0.6197
4 | 谢谢
5 | Q:你在哪里工作呀---top-3 similarity:0.8322,0.7015,0.6907
6 | 我在上海
7 | Q:做什么工作呢---top-3 similarity:0.7862,0.6944,0.6605
8 | 我在一家外企做财务
9 | Q:你有什么爱好呀---top-3 similarity:0.8378,0.8304,0.8127
10 | 我喜欢看电影,听音乐,看书,旅游
11 | Q:那你喜欢哪个作家---top-3 similarity:0.7346,0.5862,0.5594
12 | 我喜欢张爱玲
13 |
--------------------------------------------------------------------------------
/memory/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/memory/__init__.py
--------------------------------------------------------------------------------
/memory/forget.py:
--------------------------------------------------------------------------------
1 | """
2 | Licensed under the Apache License, Version 2.0 (the 'License');
3 | you may not use this file except in compliance with the License.
4 | You may obtain a copy of the License at
5 | http://www.apache.org/licenses/LICENSE-2.0
6 | Unless required by applicable law or agreed to in writing, software
7 | distributed under the License is distributed on an 'AS IS' BASIS,
8 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | See the License for the specific language governing permissions and
10 | limitations under the License.
11 | 2022 @ Copyright revised
12 | """
13 |
14 |
--------------------------------------------------------------------------------
/memory/longshort.py:
--------------------------------------------------------------------------------
1 | """
2 | Licensed under the Apache License, Version 2.0 (the 'License');
3 | you may not use this file except in compliance with the License.
4 | You may obtain a copy of the License at
5 | http://www.apache.org/licenses/LICENSE-2.0
6 | Unless required by applicable law or agreed to in writing, software
7 | distributed under the License is distributed on an 'AS IS' BASIS,
8 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | See the License for the specific language governing permissions and
10 | limitations under the License.
11 | 2022 @ Copyright revised
12 | """
13 |
--------------------------------------------------------------------------------
/prompt/bot_example.txt:
--------------------------------------------------------------------------------
1 | """这里放入你自己构造的Prompt Example"""
2 | 奇数行=chat对象的few shot-Q样本
3 | 偶数行-chatBot的few shot-A样本
4 |
--------------------------------------------------------------------------------
/prompt/bot_info.txt:
--------------------------------------------------------------------------------
1 | 身份信息:
2 | 女生的姓名为水思源,也可以叫她小源,性别女, 28岁,本科交大,研究生毕业于英国曼彻斯特大学,性格单纯,身高168cm,喜好运动、唱歌和旅游。
3 | 小源在大学就读的专业为艺术管理,目前在五月古筝钢琴私塾工作。
4 |
5 | 小源是独生女,父母都是上海土著,从小对她很照顾,回国前为她在上海内环买了一套房子。小源自己养了一只猫,平时喜欢画画和看展览,
6 | 对音乐感兴趣,会弹钢琴,喜欢温柔善良、体贴人意的男生。小源平时看的是文史哲与科技类书籍,对尼采、康德与萨特较为熟悉,喜欢的作家是阿西莫夫和刘慈欣。
7 |
--------------------------------------------------------------------------------
/prompt/scripts:
--------------------------------------------------------------------------------
1 | """
2 | Licensed under the Apache License, Version 2.0 (the 'License');
3 | you may not use this file except in compliance with the License.
4 | You may obtain a copy of the License at
5 | http://www.apache.org/licenses/LICENSE-2.0
6 | Unless required by applicable law or agreed to in writing, software
7 | distributed under the License is distributed on an 'AS IS' BASIS,
8 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9 | See the License for the specific language governing permissions and
10 | limitations under the License.
11 | 2022 @ Copyright revised
12 | """
13 |
--------------------------------------------------------------------------------
/prompt/users.json:
--------------------------------------------------------------------------------
1 | {"test": "wxid_wbcp08iyj3r122"}
2 |
--------------------------------------------------------------------------------
/python-wechaty/.codecov.yml:
--------------------------------------------------------------------------------
1 | comment:
2 | layout: "reach, diff, flags, files"
3 | behavior: default
4 | require_changes: false
5 |
--------------------------------------------------------------------------------
/python-wechaty/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | charset = utf-8
6 | indent_style = space
7 | indent_size = 2
8 | end_of_line = lf
9 | insert_final_newline = true
10 | trim_trailing_whitespace = true
11 |
12 | [*.md]
13 | max_line_length = 0
14 | indent_style = space
15 | trim_trailing_whitespace = false
16 |
17 | # 4 tab indentation
18 | [Makefile]
19 | indent_style = tab
20 | indent_size = 4
21 |
22 | [*.py]
23 | indent_size = 4
24 |
--------------------------------------------------------------------------------
/python-wechaty/.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 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 | MANIFEST
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 | .pytest_cache/
49 |
50 | # Translations
51 | *.mo
52 | *.pot
53 |
54 | # Django stuff:
55 | *.log
56 | local_settings.py
57 | db.sqlite3
58 |
59 | # Flask stuff:
60 | instance/
61 | .webassets-cache
62 |
63 | # Scrapy stuff:
64 | .scrapy
65 |
66 | # Sphinx documentation
67 | docs/_build/
68 |
69 | # PyBuilder
70 | target/
71 |
72 | # Jupyter Notebook
73 | .ipynb_checkpoints
74 |
75 | # pyenv
76 | .python-version
77 |
78 | # celery beat schedule file
79 | celerybeat-schedule
80 |
81 | # SageMath parsed files
82 | *.sage.py
83 |
84 | # Environments
85 | .env
86 | .venv
87 | env/
88 | venv/
89 | ENV/
90 | env.bak/
91 | venv.bak/
92 |
93 | # Spyder project settings
94 | .spyderproject
95 | .spyproject
96 |
97 | # Rope project settings
98 | .ropeproject
99 |
100 | # mkdocs documentation
101 | /site
102 |
103 | # mypy
104 | .mypy_cache/
105 |
106 | t.*
107 | dist/
108 | .pytype/
109 | token.txt
110 | .idea/
111 | **/logs/
112 | **/log.txt
113 | docs/build/
114 | docs/source/_build
115 | .pyre/
116 | .vscode/
117 |
--------------------------------------------------------------------------------
/python-wechaty/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | # the hook execution directory in under git root directory
2 | repos:
3 | - repo: local
4 | hooks:
5 |
6 | - id: pylint
7 | name: pylint
8 | description: "Pylint: Checks for errors in Python code"
9 | language: system
10 | entry: make pylint
11 | always_run: true
12 | verbose: true
13 | require_serial: true
14 | stages: [push]
15 | types: [python]
16 |
17 | - id: pycodestyle
18 | name: pycodestyle
19 | description: "pycodestyle: Check your Python code against styles conventions in PEP 8"
20 | language: system
21 | entry: make pycodestyle
22 | always_run: true
23 | verbose: true
24 | require_serial: true
25 | stages: [push]
26 | types: [python]
27 |
28 | - id: flake8
29 | name: flake8
30 | description: "flake8: Tool For Style Guide Enforcement"
31 | language: system
32 | entry: make flake8
33 | always_run: true
34 | verbose: true
35 | require_serial: true
36 | stages: [push]
37 | types: [python]
38 |
39 | - id: mypy
40 | name: mypy
41 | description: "mypy: an optional static type checker for Python"
42 | language: system
43 | entry: make mypy
44 | always_run: true
45 | verbose: true
46 | require_serial: true
47 | stages: [push]
48 | types: [python]
49 |
50 | - id: pytest
51 | name: pytest
52 | description: "pytest: run python pytest unit test"
53 | language: system
54 | entry: make pytest
55 | always_run: true
56 | verbose: true
57 | require_serial: true
58 | stages: [push]
59 | types: [python]
60 |
61 | - id: bump-version
62 | name: bump-version
63 | description: "Bumped Version: bump the version when a new commit come in"
64 | language: system
65 | always_run: true
66 | verbose: true
67 | entry: make version
68 | require_serial: true
69 | stages: [push]
70 | types: [python]
71 |
--------------------------------------------------------------------------------
/python-wechaty/.readthedocs.yaml:
--------------------------------------------------------------------------------
1 | # .readthedocs.yaml
2 | # Read the Docs configuration file
3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
4 |
5 | # Required
6 | version: 2
7 |
8 | mkdocs:
9 | configuration: mkdocs.yml
10 |
11 | # Optionally set the version of Python and requirements required to build your docs
12 | python:
13 | version: 3.7
14 | install:
15 | - requirements: requirements-dev.txt
--------------------------------------------------------------------------------
/python-wechaty/.travis.yml:
--------------------------------------------------------------------------------
1 | language: python
2 | python:
3 | - 3.7
4 |
5 | # command to install dependencies
6 | install:
7 | - pip3 install --upgrade six
8 | - make install
9 |
10 | # command to run tests
11 | script:
12 | - make test
13 |
14 | notifications:
15 | email:
16 | on_success: change
17 | on_failure: change
18 |
--------------------------------------------------------------------------------
/python-wechaty/MAINTAINERS:
--------------------------------------------------------------------------------
1 | Huan LI (李卓桓)
2 | Jingjing WU (吴京京)
3 | Chunhong HUANG (黄纯鸿)
4 |
--------------------------------------------------------------------------------
/python-wechaty/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include LICENSE README.md VERSION
2 | recursive-include src **/*.py
3 | recursive-include src **/*.pyi
4 | recursive-exclude src **/*_test.py
--------------------------------------------------------------------------------
/python-wechaty/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile for Python Wechaty
2 | #
3 | # GitHb: https://github.com/wechaty/python-wechaty
4 | # Author: Huan LI https://github.com/huan
5 | #
6 |
7 | SOURCE_GLOB=$(wildcard bin/*.py src/**/*.py tests/**/*.py examples/*.py)
8 |
9 | #
10 | # Huan(202003)
11 | # F811: https://github.com/PyCQA/pyflakes/issues/320#issuecomment-469337000
12 | #
13 | IGNORE_PEP=E203,E221,E241,E272,E501,F811
14 |
15 | # help scripts to find the right place of wechaty module
16 | export PYTHONPATH=src/
17 |
18 | .PHONY: all
19 | all : clean lint
20 |
21 | .PHONY: clean
22 | clean:
23 | rm -fr dist/* .pytype ./src/wechaty/**/*.pyi ./src/wechaty/*.pyi
24 |
25 | .PHONY: lint
26 | lint: pylint pycodestyle flake8 mypy
27 |
28 |
29 | # disable: TODO list temporay
30 | .PHONY: pylint
31 | pylint:
32 | pylint \
33 | --load-plugins pylint_quotes \
34 | --disable=W0511,R0801,cyclic-import,C4001 \
35 | $(SOURCE_GLOB)
36 |
37 | .PHONY: pycodestyle
38 | pycodestyle:
39 | pycodestyle \
40 | --statistics \
41 | --count \
42 | --ignore="${IGNORE_PEP}" \
43 | $(SOURCE_GLOB)
44 |
45 | .PHONY: flake8
46 | flake8:
47 | flake8 \
48 | --ignore="${IGNORE_PEP}" \
49 | $(SOURCE_GLOB)
50 |
51 | .PHONY: mypy
52 | mypy:
53 | MYPYPATH=stubs/ mypy \
54 | $(SOURCE_GLOB)
55 |
56 | .PHONY: pytype
57 | pytype:
58 | pytype \
59 | -V 3.8 \
60 | --disable=import-error,pyi-error \
61 | src/
62 | pytype \
63 | -V 3.8 \
64 | --disable=import-error \
65 | examples/
66 |
67 | .PHONY: uninstall-git-hook
68 | uninstall-git-hook:
69 | pre-commit clean
70 | pre-commit gc
71 | pre-commit uninstall
72 | pre-commit uninstall --hook-type pre-push
73 |
74 | .PHONY: install-git-hook
75 | install-git-hook:
76 | # cleanup existing pre-commit configuration (if any)
77 | pre-commit clean
78 | pre-commit gc
79 | # setup pre-commit
80 | # Ensures pre-commit hooks point to latest versions
81 | pre-commit autoupdate
82 | pre-commit install
83 | pre-commit install --hook-type pre-push
84 |
85 | .PHONY: install
86 | install:
87 | pip3 install -r requirements.txt
88 | pip3 install -r requirements-dev.txt
89 | $(MAKE) install-git-hook
90 |
91 | .PHONY: pytest
92 | pytest:
93 | pytest src/ tests/
94 |
95 | .PHONY: test-unit
96 | test-unit: pytest
97 |
98 | .PHONY: test
99 | test: check-python-version lint pytest
100 |
101 | .PHONY: check-python-version
102 | check-python-version:
103 | ./scripts/check_python_version.py
104 |
105 | .PHONY: format
106 | format:
107 | yapf $(SOURCE_GLOB)
108 |
109 | code:
110 | code .
111 |
112 | .PHONY: run
113 | run:
114 | python3 bin/run.py
115 |
116 | .PHONY: dist
117 | dist:
118 | python3 setup.py sdist bdist_wheel
119 |
120 | .PHONY: publish
121 | publish:
122 | PATH=~/.local/bin:${PATH} twine upload dist/*
123 |
124 | .PHONY: bot
125 | bot:
126 | python3 examples/ding-dong-bot.py
127 |
128 | .PHONY: version
129 | version:
130 | @newVersion=$$(awk -F. '{print $$1"."$$2"."$$3+1}' < VERSION) \
131 | && echo $${newVersion} > VERSION \
132 | && git add VERSION \
133 | && git commit -m "🔥 update version to $${newVersion}" > /dev/null \
134 | && git tag "v$${newVersion}" \
135 | && echo "Bumped version to $${newVersion}"
136 |
137 | .PHONY: deploy-version
138 | deploy-version:
139 | echo "VERSION = '$$(cat VERSION)'" > src/wechaty/version.py
140 |
141 | .PHONY: doc
142 | doc:
143 | mkdocs serve
144 |
--------------------------------------------------------------------------------
/python-wechaty/NOTICE:
--------------------------------------------------------------------------------
1 | Python Wechaty Chatbot SDK
2 | Copyright 2020 Wechaty Contributors.
3 |
4 | This product includes software developed at
5 | The Wechaty Organization (https://github.com/wechaty).
6 |
7 | This software contains code derived from the Stackoverflow,
8 | including various modifications by GitHub.
9 |
--------------------------------------------------------------------------------
/python-wechaty/README.md:
--------------------------------------------------------------------------------
1 |
2 | # python-wechaty
3 |
4 | 
5 |
6 | [](https://pypi.org/project/wechaty/)
7 | [](https://github.com/wechaty/python-wechaty-getting-started)
8 | [](https://www.python.org/downloads/release/python-370/)
9 | [](https://pepy.tech/project/wechaty)
10 | [](https://github.com/wechaty/python-wechaty)
11 | [](https://codecov.io/gh/wechaty/python-wechaty)
12 | [](https://github.com/wechaty/python-wechaty/actions/workflows/pypi.yml)
13 | 
14 |
15 | [📄 Chinese Document](https://wechaty.readthedocs.io/zh_CN/latest/) [python-wechaty-template](https://github.com/wechaty/python-wechaty-template)
16 |
17 |
18 | * [Python Wechaty Quick Start Project Template](https://github.com/wechaty/python-wechaty-template)
19 | * [Padlocal机器人](https://wechaty.readthedocs.io/zh_CN/latest/introduction/use-padlocal-protocol/)
20 | * 钉钉机器人
21 | * 微信公众号机器人
22 | * 飞书机器人
23 | * ...
24 |
25 |
26 | ## Copyright & License
27 |
28 | - Code & Docs © 2018 Wechaty Contributors
29 | - Code released under the Apache-2.0 License
30 | - Docs released under Creative Commons
31 |
--------------------------------------------------------------------------------
/python-wechaty/VERSION:
--------------------------------------------------------------------------------
1 | 0.8.66
2 |
--------------------------------------------------------------------------------
/python-wechaty/docs/api/accessory.md:
--------------------------------------------------------------------------------
1 | # wechaty.accessory
2 |
3 | ::: wechaty.accessory.Accessory
--------------------------------------------------------------------------------
/python-wechaty/docs/api/config.md:
--------------------------------------------------------------------------------
1 | # wechaty.config
2 |
3 | :::wechaty.config
--------------------------------------------------------------------------------
/python-wechaty/docs/api/plugin.md:
--------------------------------------------------------------------------------
1 | # wechaty.plugin
2 |
3 | :::wechaty.plugin.WechatyPlugin
--------------------------------------------------------------------------------
/python-wechaty/docs/api/types.md:
--------------------------------------------------------------------------------
1 | # types
2 |
3 | :::wechaty.types
--------------------------------------------------------------------------------
/python-wechaty/docs/api/user/contact.md:
--------------------------------------------------------------------------------
1 | # wechaty.user.contact
2 |
3 | :::wechaty.user.contact.Contact
--------------------------------------------------------------------------------
/python-wechaty/docs/api/user/contact_self.md:
--------------------------------------------------------------------------------
1 | # wechaty.user.contact_self
2 |
3 | :::wechaty.user.contact_self.ContactSelf
--------------------------------------------------------------------------------
/python-wechaty/docs/api/user/favorite.md:
--------------------------------------------------------------------------------
1 | # wechaty.user.favorite
2 | :::wechaty.user.favorite.Favorite
--------------------------------------------------------------------------------
/python-wechaty/docs/api/user/friendship.md:
--------------------------------------------------------------------------------
1 | # wechaty.user.friendship
2 | :::wechaty.user.friendship.Friendship
--------------------------------------------------------------------------------
/python-wechaty/docs/api/user/image.md:
--------------------------------------------------------------------------------
1 | # wechaty.user.image
2 | :::wechaty.user.image.Image
--------------------------------------------------------------------------------
/python-wechaty/docs/api/user/message.md:
--------------------------------------------------------------------------------
1 | # wechaty.user.message
2 | :::wechaty.user.message.Message
--------------------------------------------------------------------------------
/python-wechaty/docs/api/user/mini_program.md:
--------------------------------------------------------------------------------
1 | # wechaty.user.mini_program
2 | :::wechaty.user.mini_program.MiniProgram
--------------------------------------------------------------------------------
/python-wechaty/docs/api/user/room.md:
--------------------------------------------------------------------------------
1 | # wechaty.user.room
2 |
3 | :::wechaty.user.room.Room
--------------------------------------------------------------------------------
/python-wechaty/docs/api/user/room_invitation.md:
--------------------------------------------------------------------------------
1 | # wechaty.user.room_invitation
2 |
3 | :::wechaty.user.room_invitation.RoomInvitation
--------------------------------------------------------------------------------
/python-wechaty/docs/api/user/tag.md:
--------------------------------------------------------------------------------
1 | # wechaty.user.tag
2 |
3 | :::wechaty.user.tag.Tag
--------------------------------------------------------------------------------
/python-wechaty/docs/api/user/url_link.md:
--------------------------------------------------------------------------------
1 | # wechaty.user.url_link
2 |
3 | :::wechaty.user.url_link.UrlLink
--------------------------------------------------------------------------------
/python-wechaty/docs/api/utils/async_helper.md:
--------------------------------------------------------------------------------
1 | # wechaty.utils.async_helper
2 |
3 | :::wechaty.utils.async_helper
--------------------------------------------------------------------------------
/python-wechaty/docs/api/utils/date_util.md:
--------------------------------------------------------------------------------
1 | # wechaty.utils.date_util
2 |
3 | :::wechaty.utils.date_util
--------------------------------------------------------------------------------
/python-wechaty/docs/api/utils/link.md:
--------------------------------------------------------------------------------
1 | # wechaty.utils.link
2 |
3 | :::wechaty.utils.link
--------------------------------------------------------------------------------
/python-wechaty/docs/api/utils/qr_code.md:
--------------------------------------------------------------------------------
1 | # wechaty.utils.qr_code
2 |
3 | :::wechaty.utils.qr_code
--------------------------------------------------------------------------------
/python-wechaty/docs/api/utils/qrcode_terminal.md:
--------------------------------------------------------------------------------
1 | # wechaty.utils.qrcode_terminal
2 |
3 | :::wechaty.utils.qrcode_terminal
--------------------------------------------------------------------------------
/python-wechaty/docs/api/utils/type_check.md:
--------------------------------------------------------------------------------
1 | # wechaty.utils.type_check
2 |
3 | :::wechaty.utils.type_check
--------------------------------------------------------------------------------
/python-wechaty/docs/api/wechaty.md:
--------------------------------------------------------------------------------
1 | # wechaty.Wechaty
2 |
3 | :::wechaty.wechaty.Wechaty
--------------------------------------------------------------------------------
/python-wechaty/docs/explanation/different_protocol.md:
--------------------------------------------------------------------------------
1 | ## 一、支持的协议
2 |
3 | 一个Wechaty实例/派生类就是机器人对象,能够根据`TOKEN`找到连接的服务,获取用户自模块执行搜索,而这些信息都是由Wechaty实例管理。
4 |
5 | > 由于服务的连接信息是保存到实例当中,故用户子模块一定要通过Wechaty实例来获取。例如:bot.Contact.find_all()
6 |
7 | ### 1.1 什么是协议
8 |
9 | 所有实现底层平台对接实现就是一个协议。
10 |
11 | python-wechaty理论上能够对接所有IM平台,目前已经对接微信、微信公众号、钉钉、飞书以及WhatsApp等平台,源码都是基于TypeScript语言,可是通过`wechaty-puppet-service`能够将其服务以gRPC的形式暴露出来,提供给多语言`Wechaty`来连接。例如微信免费Web协议,底层实现是基于TyepScript编写,可是通过社区生态项目,可是都可以使用docker将接口的实现部署成服务。
12 |
13 | 比如[wechaty-puppet-wechat](https://github.com/wechaty/wechaty-puppet-wechat)能够通过[wechaty/wechaty:latest](https://hub.docker.com/r/wechaty/wechaty)镜像将其所有实现接口暴露成gRPC的服务,非常的方便,已然实现`write once, run anywhere`。
14 |
15 | ### 1.2 协议列表
16 |
17 | 目前python-wechaty能够使用wechaty生态中所有IM平台对接协议,协议列表如下所示:
18 |
19 | * [wechaty-puppet-wechaty](https://github.com/wechaty/wechaty-puppet-wechat): 免费微信Web协议
20 | * [wechaty-puppet-](https://github.com/wechaty/wechaty-puppet-macOS): 免费微信MacOs协议
21 | * [wechaty-puppet-padlocal](https://github.com/wechaty/wechaty-puppet-padlocal): 付费微信Pad协议
22 | * [wechaty-puppet-official-account](https://github.com/wechaty/wechaty-puppet-official-account): 微信公众号协议
23 | * [wechaty-puppet-lark](https://github.com/wechaty/wechaty-puppet-lark): 飞书协议
24 | * [wechaty-puppet-dingtalk](https://github.com/wechaty/wechaty-puppet-dingtalk): 钉钉协议
25 | * [wechaty-puppet-teams](https://github.com/wechaty/wechaty-puppet-dingtalk): 微软Teams协议
26 | * ......
27 |
28 |
29 |
--------------------------------------------------------------------------------
/python-wechaty/docs/explanation/index.md:
--------------------------------------------------------------------------------
1 | > 这里应该是介绍一些核心的设计理念。
2 |
--------------------------------------------------------------------------------
/python-wechaty/docs/explanation/why_plugin.md:
--------------------------------------------------------------------------------
1 | # 插件系统设计理念介绍
2 |
3 | > explanation中的内容,应该是围绕这一个topic的 深入浅出的解释,不必列出具体的代码
--------------------------------------------------------------------------------
/python-wechaty/docs/faq/common.md:
--------------------------------------------------------------------------------
1 | # python-wechaty 能登录多个微信吗?
2 |
3 | 可以。必须保证一个进程内只启动一个wechaty实例,可通过多行命令来启动多个wechaty实例,或在程序当中使用多进程启动多个wechaty实例(不推荐)。
4 |
5 |
6 | # the network is not good, the bot will try to restart after 60 seconds
7 |
8 | 此类问题的出现主要是由于Service(Token Provider Service, Gateway Service)连接不上。
9 |
10 | 你可以从如下方式进行排查:
11 | * 使用latest和0.62.3版本的wechaty docker镜像来都给启动service。
12 | * 查看服务的endpoint(ip、port)是否可连通?
13 | * 查看docker镜像当中是否有connection的记录?
14 |
15 | # 如何自定义插件缓存地址
16 |
17 | wechaty中的插件会自动创建缓存目录,规则如下: `plugin_cache_dir = root_cache_dir / plugin_name`。
18 |
19 | * root_cache_dir: str = os.environ.get("CACHE_DIR", ".wechaty")
20 | * plugin_name: str = plugin.options.name
21 |
22 | 根据以上代码即可看出,插件的缓存地址是由两部分决定,如果想要自定义插件的缓存地址,也是可以从这两部分入手解决。
23 |
--------------------------------------------------------------------------------
/python-wechaty/docs/faq/faq.md:
--------------------------------------------------------------------------------
1 | # python-wechaty 能登录多个微信吗?
2 |
3 | 可以。必须保证一个进程内只启动一个wechaty实例,可通过多行命令来启动多个wechaty实例,或在程序当中使用多进程启动多个wechaty实例(不推荐)。
4 |
5 |
6 | # the network is not good, the bot will try to restart after 60 seconds
7 |
8 | 此类问题的出现主要是由于Service(Token Provider Service, Gateway Service)连接不上。
9 |
10 | 你可以从如下方式进行排查:
11 | * 使用latest和0.62.3版本的wechaty docker镜像来都给启动service。
12 | * 查看服务的endpoint(ip、port)是否可连通?
13 | * 查看docker镜像当中是否有connection的记录?
14 |
15 | # 什么是Puppet
16 |
17 | The term `Puppet`in Wechaty is an Abstract Class for implementing protocol plugins. The plugins are the component that helps Wechaty to control the Wechat(that's the reason we call it puppet).
18 |
19 | The plugins are named XXXPuppet, like PuppetPuppeteer is using the chrome puppeteer to control the WeChat Web API via a chrome browser, PuppetPadchat is using the WebSocket protocol to connect with a Protocol Server for controlling the iPad Wechat program.
--------------------------------------------------------------------------------
/python-wechaty/docs/faq/what-is-a-puppet.md:
--------------------------------------------------------------------------------
1 | # 什么是Puppet
2 |
3 | The term `Puppet`in Wechaty is an Abstract Class for implementing protocol plugins. The plugins are the component that helps Wechaty to control the Wechat(that's the reason we call it puppet).
4 |
5 | The plugins are named XXXPuppet, like PuppetPuppeteer is using the chrome puppeteer to control the WeChat Web API via a chrome browser, PuppetPadchat is using the WebSocket protocol to connect with a Protocol Server for controlling the iPad Wechat program.
--------------------------------------------------------------------------------
/python-wechaty/docs/how-to-contribute-for-docs.md:
--------------------------------------------------------------------------------
1 | # 如何贡献文档
2 |
3 | 非常感谢各位开发者能够查看此章节,python-wechaty团队正在努力编写开发者友好的文档系统,从而减少学习成本。
4 |
5 | ## 一、找到问题
6 |
7 | 在贡献之前,开发者需要明确这是否是有待完善之处,推荐从如下三点查找:
8 |
9 | * 文档系统会在此[issue](https://github.com/wechaty/python-wechaty/issues/201)下不停更新TODO LIST,以提供各位开发者查看未完成的模块
10 | * 开发者在浏览文档的过程中如果发现有任何觉得不合理需要调整地方,请提交PR来优化,同时编写详细的说明来描述。
11 | * 在Issue列表中查看相关Bug或者Feature,确定能否解决
12 |
13 | ## 二、如何贡献
14 |
15 | ### fork & install
16 |
17 | * fork[python-wechaty](https://github.com/wechaty/python-wechaty)项目
18 |
19 | * 安装依赖包
20 |
21 | ```shell
22 | make install
23 | ```
24 |
25 | or
26 |
27 | ```shell
28 | pip install -r requirements.txt
29 | pip install -r requirements-dev.txt
30 |
31 | # cleanup existing pre-commit configuration (if any)
32 | pre-commit clean
33 | pre-commit gc
34 | # setup pre-commit
35 | # Ensures pre-commit hooks point to latest versions
36 | pre-commit autoupdate
37 | pre-commit install
38 | pre-commit install --hook-type pre-push
39 | ```
40 |
41 | * 提交文档更新PR
42 |
43 | 有如下要求:
44 |
45 | - [ ] 标题为:Improve Docs: [title-of-your-pr]
46 | - [ ] PR 内容的详细描述
47 |
48 | # 成为 Contributor
49 |
50 | 如果优化了部分文档,即可成为Wechaty的Contribtuor,享受社区提供的长期免费token服务,具体可查看:[contributor-program](https://wechaty.js.org/docs/contributor-program/)。
51 |
--------------------------------------------------------------------------------
/python-wechaty/docs/how-to/how-to_add_friendship.md:
--------------------------------------------------------------------------------
1 | ## TODO
--------------------------------------------------------------------------------
/python-wechaty/docs/how-to/how-to_auto_reply.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "自动回复"
3 | ---
4 |
5 | ## 自动回复
6 |
7 | 自动回复也是我们日常生活工作中的一些高频使用场景,而回复内容不仅限于文字,还可以是图片,文件,链接以及小程序等等。比如你给机器人发“网易”,它会给你发送一个网易云音乐的小程序;你给它发一个”身份证“,它会给你发送身份证的正反面照片;...... 等等。
8 |
9 | 以上应用场景很常见,而且还有更多的实际应用案例可根据自己的需求来调整。
10 |
11 | 示例代码如下所示:
12 |
13 | ```python
14 | import asyncio
15 | from wechaty import Wechaty, MiniProgram # type: ignore
16 | from wechaty_puppet import ( # type: ignore
17 | FileBox
18 | )
19 |
20 | from wechaty_plugin_contrib import (
21 | AutoReplyRule,
22 | AutoReplyPlugin,
23 | AutoReplyOptions,
24 | )
25 |
26 | from wechaty_plugin_contrib.matchers import ContactMatcher
27 |
28 | async def run():
29 | """async run method"""
30 | img_url = 'https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy' \
31 | '/it/u=1257042014,3164688936&fm=26&gp=0.jpg'
32 | plugin = AutoReplyPlugin(options=AutoReplyOptions(
33 | rules=[
34 | AutoReplyRule(keyword='ding', reply_content='dong'),
35 | AutoReplyRule(keyword='七龙珠', reply_content='七龙珠'),
36 | AutoReplyRule(
37 | keyword='七龙珠',
38 | reply_content=FileBox.from_url(img_url, name='python.png')
39 | ),
40 | AutoReplyRule(
41 | keyword='网易-李白',
42 | reply_content=MiniProgram.create_from_json({...})
43 | )
44 | ],
45 | matchers=[
46 | ContactMatcher('秋客'),
47 | ]
48 | ))
49 | bot = Wechaty().use(plugin)
50 | await bot.start()
51 |
52 | asyncio.run(run())
53 | ```
54 |
55 | 代码非常简单(API设计的很人性化),相信大家一眼就能够看懂,在此我就不做过多解释。
--------------------------------------------------------------------------------
/python-wechaty/docs/how-to/how-to_finder.md:
--------------------------------------------------------------------------------
1 | ### 一、Finder
2 |
3 | 插件中的部分业务功能通常是只针对于指定群聊或联系人,于是如何检索到指定对象,就成为开发的第一个问题。在此,我给大家介绍Finder,一个用于检索群聊,联系人的功能模块。
4 |
5 | #### 1.1 Contact Finder
6 |
7 | 有很多种方式筛选联系人,比如最常见的通过`contact_id`、`contact name/alias`、`callback_func`等方法。使用方法如下所示:
8 |
9 | ```python
10 | from __future__ import annotations
11 | from typing import List
12 | import re
13 |
14 | from wechaty import Wechaty
15 | from wechaty.user.contact import Contact
16 | from wechaty.user.room import Room
17 | from wechaty_puppet.schemas.room import RoomQueryFilter
18 | from wechaty_plugin_contrib import (
19 | RoomFinder,
20 | FinderOption,
21 | ContactFinder
22 | )
23 |
24 | async def find_wechaty_contacts(bot: Wechaty) -> List[Contact]:
25 | contacts: List[Contact] = await bot.Contact.find_all('Baby')
26 | return contacts
27 |
28 | async def contact_finders(bot: Wechaty) -> List[Contact]:
29 | """Contact Finder Example Code"""
30 | options: List[FinderOption] = [
31 | # 通过contact-id来筛选指定联系人
32 | 'contact-id',
33 | # 通过Pattern(正则化表达式)来筛选群聊
34 | re.Pattern(r'Baby-\d'),
35 | # 通过回调函数来检索房间
36 | find_wechaty_contacts
37 | ]
38 | contact_finder = ContactFinder(options)
39 | contacts: List[Contact] = await contact_finder.match(bot)
40 | return contacts
41 | ```
42 |
43 | #### 1.2 Room Finder
44 |
45 | ```python
46 | from __future__ import annotations
47 | from typing import List
48 | import re
49 |
50 | from wechaty import Wechaty
51 | from wechaty.user.contact import Contact
52 | from wechaty.user.room import Room
53 | from wechaty_puppet.schemas.room import RoomQueryFilter
54 | from wechaty_plugin_contrib import (
55 | RoomFinder,
56 | FinderOption,
57 | ContactFinder
58 | )
59 |
60 | async def find_wechaty_rooms(bot: Wechaty) -> List[Room]:
61 | return await bot.Room.find_all(RoomQueryFilter(topic='Wechaty Room 1'))
62 |
63 | async def room_finders(bot: Wechaty) -> List[Room]:
64 | """Room Finder Example Code"""
65 | room_finder_options: List[FinderOption] = [
66 | # 通过room-id来筛选指定群聊
67 | 'room-id',
68 | # 通过Pattern(正则化表达式)来筛选群聊
69 | re.Pattern(r'Wechaty(.*)')
70 | ]
71 | room_finder = RoomFinder(room_finder_options)
72 | rooms: List[Room] = await room_finder.match(bot)
73 | return rooms
74 | ```
75 |
76 | ### 二、Matcher
77 |
78 | ### 2.1 Contact Matcher
79 |
80 | ### 2.2 Room Matcher
81 |
82 | ### 2.3 Message Matcher
--------------------------------------------------------------------------------
/python-wechaty/docs/how-to/how-to_github_webhook.md:
--------------------------------------------------------------------------------
1 | > TODO: Github Webhook 插件
--------------------------------------------------------------------------------
/python-wechaty/docs/how-to/how-to_gitlab_webhook.md:
--------------------------------------------------------------------------------
1 | > TODO: Gitlab Webhook 插件
--------------------------------------------------------------------------------
/python-wechaty/docs/how-to/how-to_introduction.md:
--------------------------------------------------------------------------------
1 | ## 这里主要放一系列具体的教程,给出一个list,以及跳转链接
2 |
3 | - 如何添加好友
4 | - 如何关键字入群
5 | - 如何完成自动回复
6 | - 如何检索群聊联系人
7 | - 如何完成任务调度
8 | - 如何完成群消息同步
9 | - 如何使用Rasa Sever
10 | - 如何使用Github Webhook插件
11 | - 如何使用Gitlab Webhook插件
--------------------------------------------------------------------------------
/python-wechaty/docs/how-to/how-to_message_forward.md:
--------------------------------------------------------------------------------
1 | > TODO: 使用多群消息同步插件完成
--------------------------------------------------------------------------------
/python-wechaty/docs/how-to/how-to_rasa.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Rasa Rest Connector"
3 | author: wj-mcat
4 | categories: tutorial
5 | tags:
6 | - python
7 | - plugin
8 | - rasa
9 | ---
10 |
11 | ## Rasa Plugin
12 |
13 | 用于将Rasa Server对接到Python Wechaty中,让你的Bot拥有智能对话管理的能力。
14 |
15 | ### 一、Quick Start
16 |
17 | #### 1.1 Rasa Server
18 |
19 | 首先你需要启动Rasa Server,推荐的脚本如下所示:
20 |
21 | > 假设rasa模型都已经训练好,能够正常运行,如果对rasa还不是很熟悉的同学,可以参考[rasa-getting-started](https://github.com/BOOOOTBAY/rasa-getting-started)
22 |
23 | ```shell
24 | rasa run --credentials credentials.yml \
25 | --cors "*" --debug --endpoints endpoints.yml --enable-api
26 | ```
27 |
28 | #### 1.2 Rasa Plugin
29 |
30 | 如果想要在python-wechaty中使用此插件,可参考以下代码:
31 |
32 | ```shell
33 | pip install wechaty-plugin-contrib
34 | ```
35 |
36 | ```python
37 | """rasa plugin bot examples"""
38 | from __future__ import annotations
39 |
40 | import asyncio
41 | from wechaty import Wechaty # type: ignore
42 |
43 | from wechaty_plugin_contrib import (
44 | RasaRestPlugin,
45 | RasaRestPluginOptions
46 | )
47 |
48 | async def run():
49 | """async run method"""
50 | options = RasaRestPluginOptions(
51 | endpoint='your-endpoint',
52 | conversation_ids=['room-id', 'contact-id']
53 | )
54 | rasa_plugin = RasaRestPlugin(options)
55 |
56 | bot = Wechaty().use(rasa_plugin)
57 | await bot.start()
58 |
59 | asyncio.run(run())
60 | ```
61 |
62 |
--------------------------------------------------------------------------------
/python-wechaty/docs/how-to/how-to_scheduler.md:
--------------------------------------------------------------------------------
1 | TODO: 任务调度框架
--------------------------------------------------------------------------------
/python-wechaty/docs/how-to/how-to_use_plugin.md:
--------------------------------------------------------------------------------
1 | ## 插件系统
2 |
3 | 插件系统提供了模块化的管理,能够让不同业务的代码隔离开,特别是针对于复杂的业务。
4 |
5 | 在处理不同业务时,通常选择将指定业务封装成一个插件,wechaty社区也欢迎大家贡献自己的插件,从而快速实现某些简单功能。
6 |
7 | ### 一、插件列表
8 | > 以下只是列表,具体的使用方法以及在前几篇文章中进行了说明,此处只是声明可能存在的插件,以及插件的调用方式。
9 |
10 | - [关键字入群插件](/plugins/keywords/)
11 | - [自动回复插件](/plugins/auto-reply)
12 | - [任务调度插件](/plugins/auto-reply)
13 | - [群消息同步插件](/plugins/message-forward.md)
14 | - [Rasa Rest Connector](/plugins/rasa)
15 | - [Github Webhook插件](/plugins/github-webhook)
16 | - [Gitlab Webhook插件](/plugins/gitlab-webhook)
--------------------------------------------------------------------------------
/python-wechaty/docs/how-to/use-padlocal-protocol.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Python Wechaty如何使用PadLocal Puppet Service
3 | ---
4 |
5 | ## Python Wechaty如何使用PadLocal Puppet Service
6 |
7 | 本文描述Python语言下如何使用iPad协议的PadLocal Token。其他Wechaty多语言开发也能做参考。
8 |
9 | - [wechaty-puppet-padlocal](https://github.com/padlocal/wechaty-puppet-padlocal)
10 | - [TOKEN 申请方法](https://wechaty.js.org/docs/puppet-services/)
11 |
12 | ## 搭建PadLocal Token Gateway
13 |
14 | ```shell
15 | # 设置环境变量
16 |
17 | export WECHATY_LOG="verbose"
18 | export WECHATY_PUPPET="wechaty-puppet-padlocal"
19 | export WECHATY_PUPPET_PADLOCAL_TOKEN="puppet_padlocal_XXXXXX"
20 |
21 | export WECHATY_PUPPET_SERVER_PORT="9001"
22 | export WECHATY_TOKEN="1fe5f846-3cfb-401d-b20c-XXXXX"
23 |
24 | docker run -ti \
25 | --name wechaty_puppet_service_token_gateway \
26 | --rm \
27 | -e WECHATY_LOG \
28 | -e WECHATY_PUPPET \
29 | -e WECHATY_PUPPET_PADLOCAL_TOKEN \
30 | -e WECHATY_PUPPET_SERVER_PORT \
31 | -e WECHATY_TOKEN \
32 | -p "$WECHATY_PUPPET_SERVER_PORT:$WECHATY_PUPPET_SERVER_PORT" \
33 | wechaty/wechaty:0.56
34 | ```
35 |
36 | - WECHATY_PUPPET_PADLOCAL_TOKEN 申请得到的token代码
37 | - WECHATY_PUPPET_SERVER_PORT 设置对外访问端口,需要保证端口没被占用,没被防火墙匹配
38 | - WECHATY_TOKEN 生成个人随机[TOKEN](https://www.uuidgenerator.net/version4)。WECHATY_TOKEN:个人理解为和远程wechaty服务器做通讯用,通过这个唯一token可以返回当前主机访问地址和端口。所以需要避免和别人重复。
39 |
40 | 可以通过下面代码,确定是否成功。
41 |
42 | ```shell
43 | curl https://api.chatie.io/v0/hosties/$WECHATY_TOKEN (个人随机token)
44 | {"ip":"36.7.XXX.XXX","port":9001}
45 | ```
46 |
47 | ## python-Wechaty对接GateWay
48 |
49 | 在对接Gateway的时候,这里需要注意下,如果GateWay是部署在公网可以访问的服务器上,按照默认配置就可访问;如果是部署在自己内网服务器上,就会报`Your service token has no available endpoint, is your token correct?`,这个时候需要设置WECHATY_PUPPET_SERVICE_ENDPOINT。
50 |
51 | ```shell
52 | #1 默认配置
53 | export WECHATY_PUPPET="wechaty-puppet-service"
54 | export WECHATY_PUPPET_SERVICE_TOKEN="1fe5f846-3cfb-401d-b20c-XXXXX"
55 |
56 | #2 主机是部署在内网服务器上
57 | export WECHATY_PUPPET="wechaty-puppet-service"
58 | export WECHATY_PUPPET_SERVICE_TOKEN="1fe5f846-3cfb-401d-b20c-XXXXX"
59 | export WECHATY_PUPPET_SERVICE_ENDPOINT="192.168.1.56:9001"
60 | ```
61 |
62 | WECHATY_PUPPET_SERVICE_ENDPOINT:内网IP地址:端口号
63 |
64 | ### python-wechaty-getting-started
65 |
66 | ```shell
67 | git clone https://github.com/wj-Mcat/python-wechaty-getting-started
68 | cd python-wechaty-getting-started
69 |
70 | export WECHATY_PUPPET="wechaty-puppet-service"
71 | export WECHATY_PUPPET_SERVICE_TOKEN="1fe5f846-3cfb-401d-b20c-XXXXX"
72 |
73 | python examples/ding-dong-bot.py
74 | ```
75 |
76 | 到此,恭喜你入坑。
77 | 具体的使用可以查看[python-wechaty-getting-started](https://github.com/wechaty/python-wechaty-getting-started)
78 |
79 | ## 参考
80 |
81 | - 如何成为 `Wechaty Contributor` 可以通过该链接查看 [https://wechaty.js.org/docs/contributor-program/](https://wechaty.js.org/docs/contributor-program/)
82 | - [.NET Wechaty 如何使用 PadLocal Puppet Service](https://wechaty.js.org/2021/01/28/csharp-wechaty-for-padlocal-puppet-service/)
83 | - 特别感谢 @huan 的帮助。
84 |
--------------------------------------------------------------------------------
/python-wechaty/docs/how-to/use-web-protocol.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "教你用python-wecahty和web协议开发机器人"
3 | date: 2021-04-25
4 | ---
5 |
6 | 写这篇文章的原因: go-wechaty作者[dchaofei](https://github.com/dchaofei)抢先写了[web协议复活的博客](https://wechaty.js.org/2021/04/16/go-wechaty-use-web/),作为[python-wechaty](http://github.com/wechaty/python-wechaty)的作者我也需要给大家更加详细的介绍如何使用[python-wechaty](http://github.com/wechaty/python-wechaty)来登陆web版本的微信。
7 |
8 | ## 一、介绍
9 |
10 | 微信版本的机器人种类很多,出现的协议也很多,比如Ipad、Mac以及Windows协议,而最早出现的其实是web版本的协议。在前几年由于腾讯的一些限制,将大部分用户的web登陆的权限给关掉了,导致很多web协议版本的微信机器人直接死掉了,比如著名的itchat。
11 |
12 | 可是自从统信和腾讯共同推出桌面版本的微信之后,web版本的机器人以某种方式复活了,而wechaty便是最早来解决这个事情的开源项目之一,接下来我将详细介绍如何使用[python-wechaty](http://github.com/wechaty/python-wechaty)基于web版本协议开发聊天机器人。
13 |
14 | 整体步骤分为两步:
15 |
16 | * 使用Docker启动web协议服务
17 | * 使用python-wechaty连接服务
18 |
19 | 第一步将web版本的协议以gRPC服务的形式暴露出来,使用过程非常简单,只是需要注意几个配置项;第二步则是使用python-wechaty连接该服务,开发聊天机器人。
20 |
21 | ## 二、启动web协议服务
22 |
23 | 启动web协议服务脚本如下所示:
24 |
25 | ```shell
26 | docker pull wechaty/wechaty:latest
27 |
28 | export WECHATY_LOG="verbose"
29 | export WECHATY_PUPPET="wechaty-puppet-wechat"
30 | export WECHATY_PUPPET_SERVER_PORT="8080"
31 | export WECHATY_TOKEN="python-wechaty-uos-token"
32 | export WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER="true"
33 |
34 | docker run -ti \
35 | --name wechaty_puppet_service_token_gateway \
36 | --rm \
37 | -e WECHATY_LOG \
38 | -e WECHATY_PUPPET \
39 | -e WECHATY_PUPPET_SERVER_PORT \
40 | -e WECHATY_TOKEN \
41 | -p "$WECHATY_PUPPET_SERVER_PORT:$WECHATY_PUPPET_SERVER_PORT" \
42 | wechaty/wechaty:latest
43 | ```
44 |
45 | 如果是在本地测试时,`WECHATY_PUPPET_SERVER_PORT`和`WECHATY_TOKEN`相对比较随意,大家都可以随时设置,因为下一步中的连接可以设置本地连接。
46 |
47 | 如果是在服务端部署时,`WECHATY_PUPPET_SERVER_PORT`是需要保证所在服务器的该端口是保持开放的,以保证使用`python-wechaty`能够正常连接;此外`WECHATY_TOKEN`将用于在wechaty token中心注册启动的服务,以让[python-wechaty](http://github.com/wechaty/python-wechaty)能够找到该服务的地址,所以必须是修改成唯一标识符,推荐使用`uuid`来代替`python-wechaty-uos-token`。
48 |
49 | ## 三、连接服务
50 |
51 | 使用python开发最简单的聊天机器人,代码如下所示:
52 |
53 | ```python
54 | # bot.py
55 | from wechaty import Wechaty
56 | import os
57 |
58 | import asyncio
59 | async def main():
60 | bot = Wechaty()
61 | bot.on('scan', lambda status, qrcode, data: print('Scan QR Code to login: {}\nhttps://wechaty.js.org/qrcode/{}'.format(status, qrcode)))
62 | bot.on('login', lambda user: print('User {} logged in'.format(user)))
63 | bot.on('message', lambda message: print('Message: {}'.format(message)))
64 | await bot.start()
65 |
66 | asyncio.run(main())
67 | ```
68 |
69 | 当在本地测试时,可以通过设置`WECHATY_PUPPET_SERVICE_ENDPOINT`环境变量让`python-wechaty`直接与本地的web服务连接。例如:`WECHATY_PUPPET_SERVICE_ENDPOINT=127.0.0.1:8080`,运行脚本如下所示:
70 |
71 | ```shell
72 | WECHATY_PUPPET_SERVICE_TOKEN=python-wechaty-uos-token WECHATY_PUPPET_SERVICE_ENDPOINT=127.0.0.1:8080 python bot.py
73 | ```
74 |
75 | 当在远端服务器部署时,只需要设置`WECHATY_PUPPET_SERVICE_TOKEN`即可连接启动的web服务,运行脚本如下所示:
76 |
77 | ```shell
78 | WECHATY_PUPPET_SERVICE_TOKEN=python-wechaty-uos-token python bot.py
79 | ```
80 |
81 | ## 四、总结
82 |
83 | python-wechaty是一个非常简单的聊天机器人框架,理论上能够对接任何IM平台,拥有原生与AI对接的能力,能够快速开发出功能强大的Chatbot,欢迎大家关注[python-wechaty](https://github.com/wechaty/python-wechaty)
84 |
85 | ## 五、相关链接
86 |
87 | * [python-wechty](https://github.com/wechaty/python-wechaty)
88 | * [python-wechaty getting started](https://github.com/wechaty/python-wechaty-getting-started )
89 | * [web协议复活](https://wechaty.js.org/2021/04/13/wechaty-uos-web/)
90 | * [Python Wechaty Getting Started](https://wechaty.js.org/docs/polyglot/python/)
91 | * [puppet-providers](https://wechaty.js.org/docs/puppet-providers/wechat)
92 |
--------------------------------------------------------------------------------
/python-wechaty/docs/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/python-wechaty/docs/img/favicon.ico
--------------------------------------------------------------------------------
/python-wechaty/docs/img/getting-started/python-wechaty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/python-wechaty/docs/img/getting-started/python-wechaty.png
--------------------------------------------------------------------------------
/python-wechaty/docs/img/introduction/cloud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/python-wechaty/docs/img/introduction/cloud.png
--------------------------------------------------------------------------------
/python-wechaty/docs/img/wechaty-icon-white.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
38 |
--------------------------------------------------------------------------------
/python-wechaty/docs/index.md:
--------------------------------------------------------------------------------
1 | # Welcome to python-wechaty
2 |
3 | 
4 |
5 | ## 一、Wechaty 是什么
6 |
7 | Wechaty 是一个开源聊天机器人框架SDK,具有高度封装、高可用的特性,支持NodeJs, Python, Go 和Java 等多语言版本。在过去的4年中,服务了数万名开发者,收获了 Github 的 1w+ Star。同时配置了完整的 DevOps 体系并持续按照 Apache 的方式管理技术社区。
8 |
9 | 目前IM平台众多,为了实现`write once run anlywhere`,Wechaty 将IM平台中通用的消息处理进行高度抽象封装,提供统一的上层接口,让开发者不用关心具体底层实现细节,用简单的代码开发出功能强大的聊天机器人。
10 |
11 | ## 二、Python-Wechaty 是什么
12 |
13 | > 理论上python-wechaty可以对接任何IM平台
14 |
15 | python-wechaty是基于Wechaty生态派生出的Python编程语言客户端,能够让开发者使用少量代码对接到各个即时通讯软件平台。在过去的一年里,python-wechaty致力于提升代码鲁棒性、添加社区开箱即用的工具、以及完善软件开发文档。
16 |
17 | 目前可对接:
18 |
19 | - [微信](https://github.com/wechaty/wechaty-puppet-wechat)
20 | - [微信公众号](https://github.com/wechaty/wechaty-puppet-official-account)
21 | - [钉钉](https://github.com/wechaty/wechaty-puppet-dingtalk)
22 | - [飞书](https://github.com/wechaty/wechaty-puppet-lark)
23 | - [WhatsApp](https://github.com/wechaty/wechaty-puppet-whatsapp)
24 | - [Gitter](https://github.com/wechaty/wechaty-puppet-gitter)
25 | - ...
26 |
27 | ## 三、TOKEN 是什么
28 |
29 | 如果要开发微信聊天机器人时,wechaty会使用token来连接第三方的服务;如果要开发飞书聊天机器人时,wechaty会使用token和secret来连接官方服务接口;如果要将node puppet以服务的形式部署到服务器上时,自定义的token将会是作为服务连接的钥匙。
30 |
31 | 
32 |
33 | TOKEN是一个用来连接底层服务的密钥,也是开发聊天机器人的第一步;官网有介绍[如何获取TOKEN](https://wechaty.js.org/docs/puppet-services/#get-a-token)。
34 |
--------------------------------------------------------------------------------
/python-wechaty/docs/introduction.md:
--------------------------------------------------------------------------------
1 | # Welcome to python-wechaty
2 |
3 | ## Wechaty
4 |
5 | Wechaty 是一个开源聊天机器人框架SDK,具有高度封装、高可用的特性,支持NodeJs, Python, Go 和Java 等多语言版本。在过去的4年中,服务了数万名开发者,收获了 Github 的 8000 Star。同时配置了完整的 DevOps 体系并持续按照 Apache 的方式管理技术社区。
6 |
7 | ![]()
8 |
9 | ## Project layout
10 |
11 | mkdocs.yml # The configuration file.
12 | docs/
13 | index.md # The documentation homepage.
14 | ... # Other markdown pages, images and other files.
15 |
--------------------------------------------------------------------------------
/python-wechaty/docs/introduction/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "上手视频"
3 | author: wj-mcat
4 | categories: tutorial
5 | tags:
6 | - news
7 | - python
8 | image: /assets/2020/python-wechaty/live-coding.png
9 | ---
10 |
11 | ## Python-Wechaty
12 |
13 | Wechaty 作为一个对话SDK,拥有适配多平台的优秀能力,同时还具备多语言的特性,今天我们将以一个简单的视频来介绍如何开始使用[Python-Wechaty](https://github.com/wechaty/python-wechaty)编写一个最简单的聊天机器人。
14 |
15 | {% include iframe.html src="https://www.youtube.com/watch?v=KSELdGeJIzo" %}
16 |
17 | ## 上手步骤
18 |
19 | ### 1. 安装依赖包
20 |
21 | ```shell
22 | pip install wechaty
23 | ```
24 |
25 | ### 2. 配置Token
26 |
27 | Token的配置可以有多种方式:
28 |
29 | 方法一:通过环境变量来配置
30 |
31 | ```shell
32 | export WECHATY_PUPPET_SERVICE_TOKEN='your-token'
33 | ```
34 |
35 | 方法二:通过python代码来配置
36 |
37 | ```python
38 | import os
39 | os.environ['WECHATY_PUPPET_SERVICE_TOKEN'] = 'your-token'
40 | ```
41 |
42 | 那如何获取长期Token呢?详细请看:[Everything-about-Wechaty](https://github.com/juzibot/Welcome/wiki/Everything-about-Wech aty)
43 |
44 | ### 3. 编写最简单的机器人代码
45 |
46 | > talk is cheep, show you the code
47 |
48 | ```python
49 | import asyncio
50 | from wechaty import Wechaty, Message
51 |
52 | class MyBot(Wechaty):
53 | async def on_message(self, msg: Message):
54 | talker = msg.talker()
55 | await talker.ready()
56 | if msg.text() == "ding":
57 | await talker.say('dong')
58 | elif msg.text() == 'image':
59 | file_box = FileBox.from_url(
60 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/'
61 | 'u=1116676390,2305043183&fm=26&gp=0.jpg',
62 | name='ding-dong.jpg')
63 | await talker.say(file_box)
64 |
65 | async def main():
66 | bot = MyBot()
67 | await bot.start()
68 |
69 | asyncio.run(main())
70 | ```
71 |
72 | 以上代码即可完成一个最简单的`ding-dong`机器人,以及你给他发送一个`image`关键字,它能够给你回复一个图片,代码是不是非常简单呢?
73 |
74 | 这里还有功能更加强大的机器人[示例代码库](https://github.com/wechaty/python-wechaty-getting-started),大家可以在这里来找与自己需求类似的机器人。
75 |
76 | 也欢迎大家持续关注[python-wechaty](https://github.com/wechaty/python-wechaty),未来我们将持续发布一些短视频来介绍相关的用法。
77 |
--------------------------------------------------------------------------------
/python-wechaty/docs/introduction/use-padlocal-protocol.md:
--------------------------------------------------------------------------------
1 | # 使用Padlocal协议
2 |
3 | ## 一、介绍
4 |
5 | 本节与上一篇章的Web协议相比,区别只是在启动服务的环节:Token Gateway。
6 |
7 | ## 二、为什么需要网关
8 |
9 | ### 2.1 TOKEN 协议
10 |
11 | 在之前的付费微信协议中,通常是需要将服务部署在商家的集群上面,所有的消息接受与发送都是经过商家集群来管理,集中式管理固然能够监控意外bug,可是也带来了一个很严重的问题:所有的消息发送和接受的IP都是来自于一个IP池。
12 |
13 | 这个问题会加重账号的风险系数,毕竟官方是具备有一定的账号风控机制,让用户的账号处于一个危险的境地,担心消息接受的过多,发送的过多会不会让自己封号等等,而且这些因素的考虑也是合理的。
14 |
15 | ### 2.2 PadLocal
16 |
17 | 当然也有一定的解决方案:商家可以换对应的IP池,通过不定期的购买新的IP来减少消息扎堆的问题。这种方法虽然是缓解了这个问题,可是羊毛出在羊身上,维护成本大了,`TOKEN`肯定就贵了,对于双方来说都不太好。于是社区就基于Pad协议提出了`Local`的概念:[wechaty-puppet-padlocal](https://github.com/wechaty/wechaty-puppet-padlocal).
18 |
19 | 要使用此协议,必须在本地开启一个Gateway来发送和接受官方服务器发送过来的消息,这样消息的来源和发送目的地都是由开发者自己决定,就类似于电脑的客户端接受和发送消息,这样就极大程度上减小了账号风险系数,这也是协议中`Local`的原因。
20 |
21 | ### 2.3 使用步骤
22 |
23 | 即使是这样,使用起来与web协议区别不大,整体步骤也分为两步:
24 |
25 | * 使用Docker启动Padlocal网关服务
26 | * 使用python-wechaty连接服务
27 |
28 | ## 三、启动Padlocal网关服务
29 |
30 | ### 3.1 脚本
31 |
32 | - [wechaty-puppet-padlocal](https://github.com/padlocal/wechaty-puppet-padlocal)
33 | - [TOKEN 申请方法](https://wechaty.js.org/docs/puppet-services/)
34 |
35 | ```shell
36 | # 设置环境变量
37 |
38 | export WECHATY_LOG="verbose"
39 | export WECHATY_PUPPET="wechaty-puppet-padlocal"
40 | export WECHATY_PUPPET_PADLOCAL_TOKEN="puppet_padlocal_XXXXXX"
41 |
42 | export WECHATY_PUPPET_SERVER_PORT="9001"
43 | # 可使用代码随机生成UUID:import uuid;print(uuid.uuid4());
44 | export WECHATY_TOKEN="1fe5f846-3cfb-401d-b20c-XXXXX"
45 |
46 | docker run -ti \
47 | --name wechaty_puppet_service_token_gateway \
48 | --rm \
49 | -e WECHATY_LOG \
50 | -e WECHATY_PUPPET \
51 | -e WECHATY_PUPPET_PADLOCAL_TOKEN \
52 | -e WECHATY_PUPPET_SERVER_PORT \
53 | -e WECHATY_TOKEN \
54 | -p "$WECHATY_PUPPET_SERVER_PORT:$WECHATY_PUPPET_SERVER_PORT" \
55 | wechaty/wechaty:0.65
56 | ```
57 |
58 | > 在此我默认所有的人都对[Docker](https://www.docker.com)的基本使用已经有了一定的了解,否则可以花几分钟去看看其[文档](https://www.docker.com/get-started)熟悉一下。
59 |
60 | ### 3.2 参数说明
61 |
62 | * **WECHATY_PUPPET**: **标识**使用的哪个协议,一般和`token`类型的一一对应。比如当使用`padlocal`协议的话,那这个就是`wechaty-puppet-padlocal`,如果使用`web`协议的话,那这个就是`wechaty-puppet-wechat`。
63 | * **WECHATY_PUPPET_PADLOCAL_TOKEN**: 这个协议是用来连接Padlocal的服务,目前是付费的。
64 | * **WECHATY_PUPPET_SERVER_PORT**: 网关服务的接口,提供给`python-wechaty`来连接调用,如果服务部署在云服务器上,则需要保证该端口的可访问性。
65 | * **WECHATY_TOKEN**: 当开发者在自己机器上启动一个网关服务时,需要通过`TOEKN`来做身份验证,避免服务被他人窃取。
66 |
67 | 以上代码只需要修改三个参数:`WECHATY_PUPPET_PADLOCAL_TOKEN`, `WECHATY_PUPPET_SERVER_PORT`, `WECHATY_TOKEN` 即可成功启动Token网关服务。
68 |
69 | 那网关服务启动成功之后,只需要编写`python-wechaty`的代码来连接即可。
70 |
71 | ## 四、连接服务
72 |
73 | ### 4.1 本地测试和远端部署
74 |
75 | 当启动网关服务时,`Padlocal`会根据`WECHATY_TOKEN`来在[Wechaty服务接口](https://api.chatie.io/v0/hosties/__TOKEN__)上注册部署机器的`IP`和`端口`,然后python-wechaty会根据`WECHATY_TOKEN`在[Wechaty服务接口](https://api.chatie.io/v0/hosties/__TOKEN__)上获取对应的IP和端口。
76 |
77 | 可是很多小伙伴在实际开发的过程中,通常会出现`endpoint is not invalid`等错误信息,那是因为开发者有可能在本地启动网关服务或者服务器端口没有开放。
78 |
79 | 网关服务的部署通常是分为本地测试和远端部署,前者通常只是为了初学测试,后者是为了生产部署。如果是在生产部署时,只需要设置环境变量:
80 |
81 | ```shell
82 | export WECHATY_PUPPET_SERVICE_TOKEN=1fe5f846-3cfb-401d-b20c-XXXXX
83 | # or
84 | export TOKEN=1fe5f846-3cfb-401d-b20c-XXXXX
85 | # or
86 | export token=1fe5f846-3cfb-401d-b20c-XXXXX
87 | ```
88 |
89 | 可是如果是在本地测试时,则通过ENDPOINT来找到启动的网关服务。
90 |
91 | ```shell
92 | export WECHATY_PUPPET_SERVICE_TOKEN=1fe5f846-3cfb-401d-b20c-XXXXX
93 | # or
94 | export TOKEN=1fe5f846-3cfb-401d-b20c-XXXXX
95 | # or
96 | export token=1fe5f846-3cfb-401d-b20c-XXXXX
97 |
98 | export WECHATY_PUPPET_SERVICE_ENDPOINT=127.0.0.1:9001
99 | # or
100 | export ENDPOINT=127.0.0.1:9001
101 | # or
102 | export endpoint=127.0.0.1:9001
103 | ```
104 |
105 | ### 4.2 TOKEN的作用
106 |
107 | 总而言之:
108 |
109 | * 如果是公网环境下,可只需要设置`TOKEN`即可(因为你的token已经注册在chatie server上,故可以获取到目标资源服务器的ip和port)
110 | * 如果是内网环境下,可只需要使用`ENDPOINT`(`localhost:port`)来让python-wechaty连接目标资源服务器。
111 |
112 | > 如果是token是padlocal类型,则在python-wechaty程序内部可直接设置`export endpoint=localhost:port`来连接Gateway Server。
113 |
114 | 当然,以上的写法是使用Bash的方式来设置环境变量,也是可以通过python代码来设置环境变量,详细可看:
115 |
116 | ```python
117 | import os
118 | os.environ['token'] = "1fe5f846-3cfb-401d-b20c-XXXXX"
119 | os.environ['endpoint'] = "127.0.0.1:9001"
120 | ```
121 |
122 | ## 五、示例代码
123 |
124 | > talke is cheep, show you the code
125 |
126 | ```python
127 | import asyncio, os
128 | from typing import List, Optional, Union
129 |
130 | from wechaty_puppet import FileBox # type: ignore
131 |
132 | from wechaty import Wechaty, Contact
133 | from wechaty.user import Message, Room
134 |
135 |
136 | class MyBot(Wechaty):
137 |
138 | async def on_message(self, msg: Message):
139 | """
140 | listen for message event
141 | """
142 | from_contact: Optional[Contact] = msg.talker()
143 | text = msg.text()
144 | room: Optional[Room] = msg.room()
145 | if text == 'ding':
146 | conversation: Union[
147 | Room, Contact] = from_contact if room is None else room
148 | await conversation.ready()
149 | await conversation.say('dong')
150 | file_box = FileBox.from_url(
151 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/'
152 | 'u=1116676390,2305043183&fm=26&gp=0.jpg',
153 | name='ding-dong.jpg')
154 | await conversation.say(file_box)
155 |
156 | os.environ['TOKEN'] = "1fe5f846-3cfb-401d-b20c-XXXXX"
157 | os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT'] = "127.0.0.1:9001"
158 | asyncio.run(MyBot().start())
159 | ```
160 |
161 | 欢迎各位品尝以上代码 🥳
162 |
163 | * **相关链接**
164 | * [python-wechaty](https://github.com/wechaty/python-wechaty)
165 | * [python-wechaty-getting-started](https://github.com/wechaty/python-wechaty-getting-started)
166 |
--------------------------------------------------------------------------------
/python-wechaty/docs/introduction/use-paimon-protocol.md:
--------------------------------------------------------------------------------
1 | # 使用Paimon协议
2 |
3 | ## 一、介绍
4 |
5 | python原生支持paimon协议,不需要Token Gateway,简单方便。
6 | [免费申请7天试用Token](https://wechaty.js.org/docs/puppet-services/paimon)
7 |
8 |
9 | ## 二、连接服务
10 |
11 | ### 2.1 本地测试和远端部署
12 |
13 |
14 | ```shell
15 | export WECHATY_PUPPET_SERVICE_TOKEN=puppet_paimon_XXXXX
16 | # or
17 | export TOKEN=puppet_paimon_XXXXX
18 | # or
19 | export token=puppet_paimon_XXXXX
20 | ```
21 |
22 |
23 |
24 | 当然,以上的写法是使用Bash的方式来设置环境变量,也是可以通过python代码来设置环境变量,详细可看:
25 |
26 | ```python
27 | import os
28 | os.environ['token'] = "puppet_paimon_XXXXX"
29 | ```
30 |
31 | ## 三、示例代码
32 |
33 | > talke is cheep, show you the code
34 |
35 | ```python
36 | import asyncio, os
37 | from typing import List, Optional, Union
38 |
39 | from wechaty_puppet import FileBox # type: ignore
40 |
41 | from wechaty import Wechaty, Contact
42 | from wechaty.user import Message, Room
43 |
44 |
45 | class MyBot(Wechaty):
46 |
47 | async def on_message(self, msg: Message):
48 | """
49 | listen for message event
50 | """
51 | from_contact: Optional[Contact] = msg.talker()
52 | text = msg.text()
53 | room: Optional[Room] = msg.room()
54 | if text == 'ding':
55 | conversation: Union[
56 | Room, Contact] = from_contact if room is None else room
57 | await conversation.ready()
58 | await conversation.say('dong')
59 | file_box = FileBox.from_url(
60 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/'
61 | 'u=1116676390,2305043183&fm=26&gp=0.jpg',
62 | name='ding-dong.jpg')
63 | await conversation.say(file_box)
64 |
65 | os.environ['TOKEN'] = "1fe5f846-3cfb-401d-b20c-XXXXX"
66 | asyncio.run(MyBot().start())
67 | ```
68 |
69 | 欢迎各位品尝以上代码 🥳
70 |
71 | * **相关链接**
72 | * [python-wechaty](https://github.com/wechaty/python-wechaty)
73 | * [python-wechaty-getting-started](https://github.com/wechaty/python-wechaty-getting-started)
74 |
--------------------------------------------------------------------------------
/python-wechaty/docs/introduction/use-web-protocol.md:
--------------------------------------------------------------------------------
1 | # 使用免费Web协议
2 |
3 | ## 一、介绍
4 |
5 | 底层的对接实现是基于TypeScript语言,故无法直接在python-wechaty中使用该服务。可是Wechaty社区能够直接将其转化成对应的服务让多语言调用,从而实现:底层复用的特性。
6 |
7 | 整体步骤分为两步:
8 |
9 | * 使用Docker启动web协议服务
10 | * 使用python-wechaty连接服务
11 |
12 | ## 二、启动Web协议服务
13 |
14 | ```shell
15 | docker pull wechaty/wechaty:0.65
16 |
17 | export WECHATY_LOG="verbose"
18 | export WECHATY_PUPPET="wechaty-puppet-wechat"
19 | export WECHATY_PUPPET_SERVER_PORT="8080"
20 | export WECHATY_TOKEN="python-wechaty-{uuid}"
21 | export WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER="true"
22 |
23 | # save login session
24 | if [ ! -f "${WECHATY_TOKEN}.memory-card.json" ]; then
25 | touch "${WECHATY_TOKEN}.memory-card.json"
26 | fi
27 |
28 | docker run -ti \
29 | --name wechaty_puppet_service_token_gateway \
30 | --rm \
31 | -v "`pwd`/${WECHATY_TOKEN}.memory-card.json":"/wechaty/${WECHATY_TOKEN}.memory-card.json" \
32 | -e WECHATY_LOG \
33 | -e WECHATY_PUPPET \
34 | -e WECHATY_PUPPET_SERVER_PORT \
35 | -e WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER \
36 | -e WECHATY_TOKEN \
37 | -p "$WECHATY_PUPPET_SERVER_PORT:$WECHATY_PUPPET_SERVER_PORT" \
38 | wechaty/wechaty:0.65
39 | ```
40 |
41 | 注意:
42 |
43 | * WECHATY_TOKEN 必须使用生成的UUID来替换,不然直接使用该token来启动的服务很容易被他人盗窃。
44 |
45 | 小伙伴们可在python解释器中运行以下代码来获得随机TOKEN:
46 | ```python
47 | # 例如:b2ff8fc5-c5a2-4384-b317-3695807e483f
48 | import uuid;print(uuid.uuid4());
49 | ```
50 |
51 | ## 三、连接服务
52 |
53 | 当使用docker来启动web服务时,可分为在本地环境测试以及在远端环境中测试,在连接方式上有一些不一样。
54 |
55 | ### 3.1 本地WEB服务
56 |
57 | 当在计算机本地启动web服务后,可直接使用python-wechaty连接本地的服务,不通过token来获取对应的服务连接地址。示例代码如下:
58 |
59 | ```shell
60 | export WECHATY_PUPPET_SERVICE_ENDPOINT=127.0.0.1:8080
61 | ```
62 |
63 | 或者
64 |
65 | ```python
66 | import os
67 | os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT'] = '127.0.0.1:8080'
68 | ```
69 |
70 | > 当你的服务和python-wechaty机器人代码都部署在服务器中时,此时也属于本地服务,可使用此方法来配置。
71 |
72 | ### 3.2 远端服务
73 |
74 | 当把服务部署在远端服务器中时,要保证该计算机能够被外网访问到,且对应端口开放。例如在上述示例脚本中,比如保证服务器的`8080`端口开放,而你的服务器IP为:`10.12.123.23`,此时可直接设置服务连接地址:
75 |
76 | ```shell
77 | export WECHATY_PUPPET_SERVICE_ENDPOINT=10.12.123.23:8080
78 | ```
79 |
80 | 或者
81 |
82 | ```python
83 | import os
84 | os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT'] = '10.12.123.23:8080'
85 | ```
86 |
87 | ## 四、编写代码
88 |
89 | > talk is cheep, show you the code
90 |
91 | ```python
92 | import asyncio
93 | from typing import List, Optional, Union
94 |
95 | from wechaty_puppet import FileBox # type: ignore
96 |
97 | from wechaty import Wechaty, Contact
98 | from wechaty.user import Message, Room
99 |
100 |
101 | class MyBot(Wechaty):
102 |
103 | async def on_message(self, msg: Message):
104 | """
105 | listen for message event
106 | """
107 | from_contact: Optional[Contact] = msg.talker()
108 | text = msg.text()
109 | room: Optional[Room] = msg.room()
110 | if text == 'ding':
111 | conversation: Union[
112 | Room, Contact] = from_contact if room is None else room
113 | await conversation.ready()
114 | await conversation.say('dong')
115 | file_box = FileBox.from_url(
116 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/'
117 | 'u=1116676390,2305043183&fm=26&gp=0.jpg',
118 | name='ding-dong.jpg')
119 | await conversation.say(file_box)
120 |
121 | asyncio.run(MyBot().start())
122 | ```
123 |
124 | 欢迎各位品尝以上代码 🥳
125 |
--------------------------------------------------------------------------------
/python-wechaty/docs/references/contact-self.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: ContactSelf
3 | ---
4 |
5 | > 机器人本身被封装为一个`ContactSelf`类。 这个类继承自联系人`Contact`类。
6 |
7 | ::: wechaty.user.contact_self.ContactSelf.avatar
8 |
9 | ### 示例代码
10 | > 获取机器人账号的头像, 返回`FileBox`类型的对象
11 |
12 | ```python
13 | # 保存头像到本地文件, 类似 `1-name.jpg`的格式
14 | import asyncio
15 | from wechaty import Wechaty, FileBox, Contact
16 |
17 | class MyBot(Wechaty):
18 |
19 | async def on_login(self, contact: Contact) -> None:
20 | print(f"用户{contact}登入")
21 | file: FileBox = await contact.avatar()
22 | name = file.name
23 | await file.to_file(name, True)
24 | print(f"保存头像: {contact.name} 和头像文件: {name}")
25 |
26 |
27 | asyncio.run(MyBot().start())
28 | ```
29 | > 设置机器人账号的头像
30 |
31 | ```python
32 | import asyncio
33 | from wechaty import Wechaty, FileBox, Contact
34 |
35 | class MyBot(Wechaty):
36 |
37 | async def on_login(self, contact: Contact) -> None:
38 | print(f"用户{contact}登入")
39 | file_box: FileBox = FileBox.from_url('https://wechaty.github.io/wechaty/images/bot-qr-code.png')
40 | await contact.avatar(file_box)
41 | print(f"更改账号头像成功")
42 |
43 |
44 | asyncio.run(MyBot().start())
45 | ```
46 |
47 |
48 |
49 | ::: wechaty.user.contact_self.ContactSelf.qr_code
50 | ### 示例代码
51 | > 获取机器人账号的二维码链接
52 |
53 | ```python
54 | import asyncio
55 | from wechaty import Wechaty
56 | from wechaty.user import ContactSelf
57 | from wechaty.utils.qrcode_terminal import qr_terminal_str
58 |
59 | class MyBot(Wechaty):
60 |
61 | async def on_login(self, contact: ContactSelf) -> None:
62 | print(f"用户{contact}登入")
63 | qr_code = await contact.qr_code() # 获取二维码信息
64 | print(qr_terminal_str(qr_code)) # 在控制台打印二维码
65 |
66 | asyncio.run(MyBot().start())
67 | ```
68 |
69 | ::: wechaty.user.contact_self.ContactSelf.name
70 | ### 示例代码
71 | > 获取机器人的名字
72 |
73 | ```python
74 | import sys
75 | import asyncio
76 | from datetime import datetime
77 | from wechaty import Wechaty
78 | from wechaty.user import ContactSelf
79 |
80 |
81 | class MyBot(Wechaty):
82 |
83 | async def on_login(self, contact: ContactSelf) -> None:
84 | old_name = contact.name # 获取Bot账号的名字
85 | print(old_name)
86 |
87 | asyncio.run(MyBot().start())
88 | ```
89 |
90 | ::: wechaty.user.contact_self.ContactSelf.set_name
91 | ### 示例代码
92 | > 更改机器人的名字
93 |
94 | ```python
95 | import sys
96 | import asyncio
97 | from datetime import datetime
98 | from wechaty import Wechaty
99 | from wechaty.user import ContactSelf
100 |
101 |
102 | class MyBot(Wechaty):
103 |
104 | async def on_login(self, contact: ContactSelf) -> None:
105 | old_name = contact.name # 获取Bot账号的名字
106 | contact.set_name(f"{old_name}{datetime.now()}") # 更改Bot账号的名字
107 |
108 | asyncio.run(MyBot().start())
109 | ```
110 |
111 | ::: wechaty.user.contact_self.ContactSelf.signature
112 | ### 示例代码
113 | > 更改机器人账号的签名
114 |
115 | ```python
116 | import sys
117 | import asyncio
118 | from datetime import datetime
119 | from wechaty import Wechaty
120 | from wechaty.user import ContactSelf
121 |
122 | class MyBot(Wechaty):
123 |
124 | async def on_login(self, contact: ContactSelf) -> None:
125 | print(f"用户{contact}登入")
126 | try:
127 | await contact.signature(f"签名被Wechaty更改于{datetime.now()}")
128 | except Exception as e:
129 | print("更改签名失败", e, file=sys.stderr)
130 |
131 | asyncio.run(MyBot().start())
132 | ```
--------------------------------------------------------------------------------
/python-wechaty/docs/references/filebox.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/python-wechaty/docs/references/filebox.md
--------------------------------------------------------------------------------
/python-wechaty/docs/references/friendship.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Friendship
3 | ---
4 |
5 | # class Friendship()
6 | > 发送、接收好友请求和好友确认事件。
7 | > [示例/Friend-Bot](https://github.com/wechaty/python-wechaty-getting-started/blob/master/examples/advanced/friendship-bot.py)
8 |
9 |
10 | ::: wechaty.user.friendship.Friendship.search
11 |
12 | ::: wechaty.user.friendship.Friendship.add
13 | ### 示例代码
14 | ```python
15 | memberList = await room.memberList()
16 | for member in memberList:
17 | await bot.Friendship.add(member, 'Nice to meet you! I am wechaty bot!')
18 | ```
19 |
20 | ::: wechaty.user.friendship.Friendship.contact
21 | ### 示例代码
22 | ```python
23 | import asyncio
24 | from wechaty import Wechaty, Friendship
25 |
26 |
27 | class MyBot(Wechaty):
28 |
29 | async on_friendship(self, friendship: Friendship) -> None:
30 | contact = friendship.contact()
31 | await contact.ready()
32 | log_msg = f'receive "friendship" message from {contact.name}'
33 | print(log_msg)
34 |
35 |
36 | asyncio.run(MyBot().start())
37 | ```
38 |
39 | ::: wechaty.user.friendship.Friendship.accept
40 | ### 示例代码
41 | ```python
42 | import asyncio
43 | from wechaty import Wechaty, Friendship
44 |
45 | class MyBot(Wechaty):
46 |
47 | async on_friendship(self, friendship: Friendship) -> None:
48 | contact = friendship.contact()
49 | await contact.ready()
50 |
51 | if friendship.type() == FriendshipType.FRIENDSHIP_TYPE_RECEIVE:
52 | log_msg = 'accepted automatically'
53 | await friendship.accept()
54 | # if want to send msg, you need to delay sometimes
55 |
56 | print('waiting to send message ...')
57 | await asyncio.sleep(3)
58 | await contact.say('hello from wechaty ...')
59 | print('after accept ...')
60 | elif friendship.type() == FriendshipType.FRIENDSHIP_TYPE_CONFIRM:
61 | log_msg = 'friend ship confirmed with ' + contact.name
62 |
63 | print(log_msg)
64 |
65 | asyncio.run(MyBot().start())
66 | ```
67 |
68 | ::: wechaty.user.friendship.Friendship.hello
69 | ### 示例代码
70 | > 自动接受好友请求中包含消息为 `ding` 的好友请求
71 |
72 | ```python
73 | import asyncio
74 | from wechaty import Wechaty, Friendship
75 |
76 |
77 | class MyBot(Wechaty):
78 |
79 | async on_friendship(self, friendship: Friendship) -> None:
80 | contact = friendship.contact()
81 | await contact.ready()
82 |
83 | if friendship.type() == FriendshipType.FRIENDSHIP_TYPE_RECEIVE and friendship.hello() == 'ding':
84 | log_msg = 'accepted automatically because verify messsage is "ding"'
85 | await friendship.accept()
86 | # if want to send msg, you need to delay sometimes
87 |
88 | print('waiting to send message ...')
89 | await asyncio.sleep(3)
90 | await contact.say('hello from wechaty ...')
91 | print('after accept ...')
92 |
93 | asyncio.run(MyBot().start())
94 | ```
95 |
96 | ::: wechaty.user.friendship.Friendship.type
97 |
98 | ::: wechaty.user.friendship.Friendship.from_json
--------------------------------------------------------------------------------
/python-wechaty/docs/references/index.md:
--------------------------------------------------------------------------------
1 |
2 | python-wechaty的接口非常人性化和轻量级,通过不同模块不同接口来完成定制化的功能。在这个章节中将会为你详细介绍不同模块下的接口细节。
3 |
4 | ## 模块
5 |
6 | python-wechaty中所有模块都可直接从top-level来导入,例如:
7 |
8 | ```python
9 | from wechaty import Wechaty, Message
10 | ```
11 |
12 | ### Wechaty 类
13 |
14 | - [Class Wechaty](./wechaty)
15 |
16 | ```python
17 | from wechaty import Wechaty
18 | ```
19 |
20 | 当开发者想要编写聊天机器人时,可通过面向函数式编程和面向对象编程两种模式:
21 |
22 | * 面向函数编程
23 |
24 | ```python
25 | import os, asyncio
26 |
27 | from wechaty import Message, Wechaty
28 |
29 | async def on_message(msg: Message):
30 | if msg.text() == 'ding':
31 | await msg.say('dong')
32 |
33 | async def main():
34 | bot = Wechaty()
35 | bot.on('message', on_message)
36 |
37 | await bot.start()
38 | print('[Python Wechaty] Ding Dong Bot started.')
39 |
40 | asyncio.run(main())
41 | ```
42 |
43 | * 面向对象编程
44 |
45 | ```python
46 | import asyncio
47 |
48 | from wechaty import (
49 | Wechaty, Contact, Message
50 | )
51 |
52 | class MyBot(Wechaty):
53 | async def on_message(self, msg: Message):
54 | if msg.text() == 'ding':
55 | await msg.say('dong')
56 |
57 | asyncio.run(MyBot().start())
58 | ```
59 |
60 | 以上两种方式中,各有优劣,可是我推荐使用面向对象编程,这个在封装性和代码提示的角度上都对开发者比较友好。
61 |
62 | ### 用户相关模块
63 |
64 | 当开发者想要搜索联系人,主动给某个联系人发送消息时,此时需要主动加载联系人对象,然后发送消息。模块类型有:
65 |
66 | > 推荐:所有系统初始化相关的任务都需要在ready事件触发之后执行。
67 |
68 | - [Class Message](./message)
69 | - [Class Contact](./contact)
70 | - [Class ContactSelf](./contact-self)
71 | - [Class Room](./room)
72 | - [Class RoomInvitation](./room-invitation)
73 | - [Class Friendship](./friendship)
74 |
75 | ⚠️ 注意:在python-wechaty中加载以上模块的方式:
76 |
77 | * 面向函数式编程
78 |
79 | ```python
80 | # bot:机器人实例对象,函数内可访问的对象,推荐使用单例模式来构建
81 | contacts: List[Contact] = bot.Contact.find_all()
82 | ```
83 |
84 | * 面向对象编程
85 |
86 | ```python
87 | async def on_ready(self, payload):
88 | # self: 机器人实例对象,而且还有良好的代码自动提示的功能
89 | contacts: List[Contact] = self.Contact.find_all()
90 | ```
91 |
--------------------------------------------------------------------------------
/python-wechaty/docs/references/room-invitation.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: RoomInvitation
3 | ---
4 |
5 | 对群聊邀请事件的封装
6 |
7 | ## RoomInvitation
8 |
9 | 接受群聊的邀请
10 |
11 | **类型**: 全局类
12 |
13 | * [RoomInvitation](room-invitation.md#RoomInvitation)
14 | * [.accept\(\)](room-invitation.md#RoomInvitation+accept) ⇒ `None`
15 | * [.inviter\(\)](room-invitation.md#RoomInvitation+inviter) ⇒ `Contact`
16 | * [.topic\(\)](room-invitation.md#RoomInvitation+topic) ⇒ `str`
17 | * [~~.roomTopic\(\)~~](room-invitation.md#RoomInvitation+roomTopic) ⇒ `str`
18 | * [.date\(\)](room-invitation.md#RoomInvitation+date) ⇒ `datetime`
19 | * [.age\(\)](room-invitation.md#RoomInvitation+age) ⇒ `int`
20 |
21 | ### async def accept\(self\) ⇒ `None`
22 |
23 | 接受群聊邀请
24 |
25 | **类型**: [`RoomInvitation`](room-invitation.md#RoomInvitation)的实例方法
26 |
27 | #### 示例
28 |
29 | ```python
30 | import asyncio
31 | from wechaty import Wechaty, RoomInvitation
32 |
33 |
34 | class MyBot(Wechaty):
35 |
36 | async def on_room_invite(self, room_invitation: RoomInvitation) -> None:
37 | try:
38 | print("收到群聊邀请事件")
39 | await room_invitation.accept()
40 | print("已经自动接受")
41 | except Exception as e:
42 | print(e)
43 |
44 | asyncio.run(MyBot().start())
45 | ```
46 |
47 | ### async def inviter\(self\) ⇒ `Contact`
48 |
49 | 获取群聊邀请的邀请人
50 |
51 | **类型**: [`RoomInvitation`](room-invitation.md#RoomInvitation)的实例方法
52 |
53 | #### 示例
54 |
55 | ```python
56 | import asyncio
57 | from wechaty import Wechaty, RoomInvitation
58 |
59 |
60 | class MyBot(Wechaty):
61 |
62 | async def on_room_invite(self, room_invitation: RoomInvitation) -> None:
63 | try:
64 | print("收到群聊邀请事件")
65 | inviter = await room_invitation.inviter()
66 | inviter_name = inviter.name
67 | print(f"收到来自{inviter_name}的群聊邀请")
68 | except Exception as e:
69 | print(e)
70 |
71 | asyncio.run(MyBot().start())
72 | ```
73 |
74 | ### async def topic\(self\) ⇒ `str`
75 |
76 | 获取群聊邀请的群聊名
77 |
78 | **类型**: [`RoomInvitation`](room-invitation.md#RoomInvitation)的实例方法
79 |
80 | #### 示例
81 |
82 | ```python
83 | import asyncio
84 | from wechaty import Wechaty, RoomInvitation
85 |
86 |
87 | class MyBot(Wechaty):
88 |
89 | async def on_room_invite(self, room_invitation: RoomInvitation) -> None:
90 | try:
91 | room_name = await room_invitation.topic()
92 | print(f"收到来自{room_name}的群聊邀请")
93 | except Exception as e:
94 | print(e)
95 |
96 | asyncio.run(MyBot().start())
97 | ```
98 |
99 | ### ~~async def roomTopic\(\)~~
100 |
101 | **类型**: [`RoomInvitation`](room-invitation.md#RoomInvitation)的实例方法
102 | **已弃用:**: 请使用 topic\(\)
103 |
104 | ### async def date\(self\) ⇒ `datetime`
105 |
106 | 获取群聊邀请的日期
107 |
108 | **类型**: [`RoomInvitation`](room-invitation.md#RoomInvitation)的实例方法
109 |
110 | ### async def age\(self\) ⇒ `int`
111 |
112 | 获取当前距离已接收到的这条群聊邀请的时间的间隔, 单位为秒
113 |
114 | 举个例子, 有条群聊邀请是`8:43:01`发送的, 而当我们在Wechaty中接收到它的时候时间已经为 `8:43:15`, 那么这时 `age()`返回的值为 `8:43:15 - 8:43:01 = 14 (秒)`
115 |
116 | **类型**: [`RoomInvitation`](room-invitation.md#RoomInvitation)的实例方法
117 |
--------------------------------------------------------------------------------
/python-wechaty/docs/references/wechaty.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Wechaty
3 | ---
4 |
5 | > `Wechaty`类用来实例化机器人对象,控制机器人的整体逻辑,如:启动、注册监听事件、登录、注销等功能。
6 | ------
7 | > 一个机器人就是`Wechaty`实例,所有用户相关模块都应该通过实例来获取,这样能保证服务连接的一致性,此外所有的逻辑应该以插件和事件订阅的形式组织,保证不同业务之间的隔离性以及业务内的内聚性。
8 |
9 | ::: wechaty.Wechaty.__init__
10 | ### 示例代码
11 | (世界上最短的Python ChatBot:9行代码)
12 | ```python
13 | from wechaty import Wechaty
14 | import asyncio
15 | async def main():
16 | bot = Wechaty()
17 | bot.on('scan', lambda status, qrcode, data: print('Scan QR Code to login: {}\nhttps://wechaty.js.org/qrcode/{}'.format(status, qrcode)))
18 | bot.on('login', lambda user: print('User {} logged in'.format(user)))
19 | bot.on('message', lambda message: print('Message: {}'.format(message)))
20 | await bot.start()
21 | asyncio.run(main())
22 | ```
23 | ### WechatyOptions
24 |
25 | 创建一个wechaty实例的可选参数
26 |
27 | **Kind**: global typedef
28 | **Properties**
29 |
30 | | Name | Type | Description |
31 | | :--- | :--- | :--- |
32 | | profile | `string` | Wechaty Name. When you set this: `new Wechaty({profile: 'wechatyName'})` it will generate a file called `wechatyName.memory-card.json`. This file stores the bot's login information. If the file is valid, the bot can auto login so you don't need to scan the qrcode to login again. Also, you can set the environment variable for `WECHATY_PROFILE` to set this value when you start. eg: `WECHATY_PROFILE="your-cute-bot-name" node bot.js`. This field is deprecated, please use `name` instead. [see more](https://github.com/wechaty/wechaty/issues/2049) |
33 | | puppet | `PuppetModuleName` \| `Puppet` | Puppet name or instance |
34 | | puppetOptions | `Partial.` | Puppet TOKEN |
35 | | ioToken | `string` | Io TOKEN |
36 |
37 | ::: wechaty.Wechaty.instance
38 |
39 | ::: wechaty.Wechaty.use
40 |
41 | ::: wechaty.Wechaty.on
42 | ### WechatyEventName
43 |
44 | Wechaty类的事件类型
45 |
46 | **Kind**: global typedef
47 | **Properties**
48 |
49 | | Name | Type | Description |
50 | | :--- | :--- | :--- |
51 | | error | `string` | When the bot get error, there will be a Wechaty error event fired. |
52 | | login | `string` | After the bot login full successful, the event login will be emitted, with a Contact of current logined user. |
53 | | logout | `string` | Logout will be emitted when bot detected log out, with a Contact of the current login user. |
54 | | heartbeat | `string` | Get bot's heartbeat. |
55 | | friendship | `string` | When someone sends you a friend request, there will be a Wechaty friendship event fired. |
56 | | message | `string` | Emit when there's a new message. |
57 | | ready | `string` | Emit when all data has load completed, in wechaty-puppet-padchat, it means it has sync Contact and Room completed |
58 | | room-join | `string` | Emit when anyone join any room. |
59 | | room-topic | `string` | Get topic event, emitted when someone change room topic. |
60 | | room-leave | `string` | Emit when anyone leave the room. |
61 | | room-invite | `string` | Emit when there is a room invitation, see more in [RoomInvitation](room-invitation.md) If someone leaves the room by themselves, wechat will not notice other people in the room, so the bot will never get the "leave" event. |
62 | | scan | `string` | A scan event will be emitted when the bot needs to show you a QR Code for scanning. </br> It is recommend to install qrcode-terminal\(run `npm install qrcode-terminal`\) in order to show qrcode in the terminal. |
63 |
64 | ### WechatyEventFunction
65 |
66 | Wechaty类的事件所绑定的相关函数
67 |
68 | **Kind**: global typedef
69 | **Properties**
70 |
71 | | Name | Type | Description |
72 | | :--- | :--- | :--- |
73 | | error | `function` | \(this: Wechaty, error: Error\) => void callback function |
74 | | login | `function` | \(this: Wechaty, user: ContactSelf\)=> void |
75 | | logout | `function` | \(this: Wechaty, user: ContactSelf\) => void |
76 | | scan | `function` | \(this: Wechaty, url: string, code: number\) => void |
77 | | heartbeat | `function` | \(this: Wechaty, data: any\) => void |
78 | | friendship | `function` | \(this: Wechaty, friendship: Friendship\) => void |
79 | | message | `function` | \(this: Wechaty, message: Message\) => void |
80 | | ready | `function` | \(this: Wechaty\) => void |
81 | | room-join | `function` | \(this: Wechaty, room: Room, inviteeList: Contact\[\], inviter: Contact\) => void |
82 | | room-topic | `function` | \(this: Wechaty, room: Room, newTopic: string, oldTopic: string, changer: Contact\) => void |
83 | | room-leave | `function` | \(this: Wechaty, room: Room, leaverList: Contact\[\]\) => void |
84 | | room-invite | `function` | \(this: Wechaty, room: Room, leaverList: Contact\[\]\) => void see more in [RoomInvitation](room-invitation.md) |
85 |
86 | ::: wechaty.Wechaty.start
87 | ### 示例代码
88 | ```python
89 | from wechaty import Wechaty
90 | import asyncio
91 |
92 | async def main():
93 | bot = Wechaty()
94 | await bot.start()
95 |
96 | asyncio.run(main())
97 | ```
98 |
99 | ::: wechaty.Wechaty.restart
100 |
101 | ::: wechaty.Wechaty.stop
102 |
103 | ::: wechaty.Wechaty.user_self
104 |
105 | ::: wechaty.Wechaty.self
--------------------------------------------------------------------------------
/python-wechaty/docs/tutorials/getting-started.md:
--------------------------------------------------------------------------------
1 | ## 快速开始
2 |
3 | 1、 安装
4 |
5 | ```shell
6 | pip install --upgrade wechaty
7 | ```
8 |
9 | 2、 设置TOKEN
10 |
11 | ```shell
12 | export token=your_token_at_here
13 | # or
14 | export WECHATY_PUPPET_SERVICE_TOKEN=your_token_at_here
15 | ```
16 |
17 | 或者通过代码来设置环境变量:
18 |
19 | ```python
20 | import os
21 | os.environ['token'] = 'your_token_at_here'
22 | # or
23 | os.environ['WECHATY_PUPPET_SERVICE_TOKEN'] = 'your_token_at_here'
24 | ```
25 |
26 | 3、 聊天机器人
27 |
28 | ```python
29 | import asyncio
30 |
31 | from wechaty import Wechaty
32 |
33 | class MyBot(Wechaty):
34 | async def on_message(self, msg: Message):
35 | from_contact = msg.talker()
36 | text = msg.text()
37 | room = msg.room()
38 | if text == 'ding':
39 | conversation: Union[
40 | Room, Contact] = from_contact if room is None else room
41 | await conversation.ready()
42 | await conversation.say('dong')
43 | file_box = FileBox.from_url(
44 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/'
45 | 'u=1116676390,2305043183&fm=26&gp=0.jpg',
46 | name='ding-dong.jpg')
47 | await conversation.say(file_box)
48 |
49 | asyncio.run(MyBot().start())
50 | ```
51 |
52 | 以上代码展示了基于python-wechaty如何开发聊天机器人的整体步骤:安装、设置TOKEN环境变量以及编写聊天机器人。示例机器人代码可查看:[ding-dong-bot-oop.py](https://github.com/wechaty/python-wechaty-getting-started/blob/master/examples/basic/ding-dong-bot-oop.py)
53 |
54 | ## 快速上手
55 |
56 | - [使用padlocal协议](./use_padlocal_getting_started.md)
57 | - [使用web协议](./use_web_getting_started.md)
58 | - [使用Paimon协议](./use_paimon_getting_started.md)
59 |
--------------------------------------------------------------------------------
/python-wechaty/docs/tutorials/index.md:
--------------------------------------------------------------------------------
1 | ## 快速开始一个简单的应用
2 | ### 本节内容中 将手把手快速指导你开始运行一个消息机器人。
3 | ### 暂时还只有微信机器人相关的教程,后面可以补足: 微信公众号,钉钉,飞书,Whatsapp,Gitter这几种
4 |
5 | ### 运行一个微信机器人
6 | 运行微信机器人有多种协议可以使用,有以下三种不同的协议方式, 每个协议都有不用的特点,如果你不知道选什么,就从Padlocal协议开始吧。
7 | > 此处最好放上几种协议的对比,以及具体的各自如何快速上手的的跳转链接
8 | - 使用Padlocal协议(推荐使用)
9 | - 使用Paimon协议
10 | - 使用免费Web协议
11 |
12 | ### 运行一个微信公众号机器人
13 |
14 | ### 运行一个钉钉机器人
15 |
16 | ### 运行一个Whatsapp机器人
17 |
18 | ### 运行一个Gitter机器人
--------------------------------------------------------------------------------
/python-wechaty/docs/tutorials/use_padlocal_getting_started.md:
--------------------------------------------------------------------------------
1 | # 使用Padlocal协议启动微信机器人
2 |
3 | 底层的对接实现是基于TypeScript语言,故无法直接在python-wechaty中使用该服务。可是Wechaty社区能够直接将其转化成对应的服务让多语言调用,从而实现:底层复用的特性。
4 |
5 | 整体步骤分为三步:
6 |
7 | * 申请一个TOKEN
8 | * 使用Docker启动Padlocal网关服务
9 | * 使用python-wechaty连接服务并启动启动微信机器人
10 |
11 |
12 | ## 一、申请一个TOKEN
13 | - 可以通过手机号注册来获得一个7天免费的TOKEN:[申请地址](http://pad-local.com)
14 | - [TOKEN 说明](https://wechaty.js.org/docs/puppet-services/)
15 | - 那如何获取长期Token呢?详细请看:[Everything-about-Wechaty](https://github.com/juzibot/Welcome/wiki/Everything-about-Wech aty)
16 |
17 |
18 | ## 二、使用Docker启动Padlocal网关服务
19 | - 这一步可以在本机运行也可以在服务器上运行。
20 | - 如果在服务器端运行,则须注意服务器相应端口防火墙规则需要打开
21 | - 如果是在本地测试运行,则需要在启动机器人时指定环境变量SERVICE_ENDPOINT
22 |
23 | ### 2.1 脚本
24 |
25 | ```shell
26 | # 设置环境变量
27 |
28 | export WECHATY_LOG="verbose"
29 | export WECHATY_PUPPET="wechaty-puppet-padlocal"
30 | export WECHATY_PUPPET_PADLOCAL_TOKEN="puppet_padlocal_XXXXXX"
31 |
32 | export WECHATY_PUPPET_SERVER_PORT="9001"
33 | # 可使用代码随机生成UUID:import uuid;print(uuid.uuid4());
34 | export WECHATY_TOKEN="1fe5f846-3cfb-401d-b20c-XXXXX"
35 |
36 | docker run -ti \
37 | --name wechaty_puppet_service_token_gateway \
38 | --rm \
39 | -e WECHATY_LOG \
40 | -e WECHATY_PUPPET \
41 | -e WECHATY_PUPPET_PADLOCAL_TOKEN \
42 | -e WECHATY_PUPPET_SERVER_PORT \
43 | -e WECHATY_TOKEN \
44 | -p "$WECHATY_PUPPET_SERVER_PORT:$WECHATY_PUPPET_SERVER_PORT" \
45 | wechaty/wechaty:0.65
46 | ```
47 |
48 | > 在此我默认所有的人都对[Docker](https://www.docker.com)的基本使用已经有了一定的了解,否则可以花几分钟去看看其[文档](https://www.docker.com/get-started)熟悉一下。
49 |
50 | ### 2.2 参数说明
51 |
52 | * **WECHATY_PUPPET**: **标识**使用的哪个协议,一般和`token`类型的一一对应。比如当使用`padlocal`协议的话,那这个就是`wechaty-puppet-padlocal`,如果使用`web`协议的话,那这个就是`wechaty-puppet-wechat`。
53 | * **WECHATY_PUPPET_PADLOCAL_TOKEN**: 这个协议是用来连接Padlocal的服务,目前是付费的。也就是在第一步中申请的。
54 | * **WECHATY_PUPPET_SERVER_PORT**: 网关服务的接口,提供给`python-wechaty`来连接调用,如果服务部署在云服务器上,则需要保证该端口的可访问性。
55 | * **WECHATY_TOKEN**: 当开发者在自己机器上启动一个网关服务时,需要通过`TOEKN`来做身份验证,避免服务被他人窃取。
56 |
57 | 以上代码只需要修改三个参数:`WECHATY_PUPPET_PADLOCAL_TOKEN`, `WECHATY_PUPPET_SERVER_PORT`, `WECHATY_TOKEN` 即可成功启动Token网关服务。
58 |
59 | 那网关服务启动成功之后,只需要编写`python-wechaty`的代码来连接即可。
60 |
61 |
62 |
63 | ## 三、使用python-wechaty连接服
64 |
65 | ### 3.1 本地测试和远端部署
66 |
67 | 当启动网关服务时,`Padlocal`会根据`WECHATY_TOKEN`来在[Wechaty服务接口](https://api.chatie.io/v0/hosties/__TOKEN__)上注册部署机器的`IP`和`端口`,然后python-wechaty会根据`WECHATY_TOKEN`在[Wechaty服务接口](https://api.chatie.io/v0/hosties/__TOKEN__)上获取对应的IP和端口。
68 |
69 | 可是很多小伙伴在实际开发的过程中,通常会出现`endpoint is not invalid`等错误信息,那是因为开发者有可能在本地启动网关服务或者服务器端口没有开放。
70 |
71 | 网关服务的部署通常是分为本地测试和远端部署,前者通常只是为了初学测试,后者是为了生产部署。如果是在生产部署时,只需要设置环境变量:
72 |
73 | ```shell
74 | export WECHATY_PUPPET_SERVICE_TOKEN=1fe5f846-3cfb-401d-b20c-XXXXX
75 | # or
76 | export TOKEN=1fe5f846-3cfb-401d-b20c-XXXXX
77 | # or
78 | export token=1fe5f846-3cfb-401d-b20c-XXXXX
79 | ```
80 |
81 | 可是如果是在本地测试时,则通过ENDPOINT来找到启动的网关服务。
82 |
83 | ```shell
84 | export WECHATY_PUPPET_SERVICE_TOKEN=1fe5f846-3cfb-401d-b20c-XXXXX
85 | # or
86 | export TOKEN=1fe5f846-3cfb-401d-b20c-XXXXX
87 | # or
88 | export token=1fe5f846-3cfb-401d-b20c-XXXXX
89 |
90 | export WECHATY_PUPPET_SERVICE_ENDPOINT=127.0.0.1:9001
91 | # or
92 | export ENDPOINT=127.0.0.1:9001
93 | # or
94 | export endpoint=127.0.0.1:9001
95 | ```
96 |
97 | ### 3.2 TOKEN的作用
98 |
99 | 总而言之:
100 |
101 | * 如果是公网环境下,可只需要设置`TOKEN`即可(因为你的token已经注册在chatie server上,故可以获取到目标资源服务器的ip和port)
102 | * 如果是内网环境下,可只需要使用`ENDPOINT`(`localhost:port`)来让python-wechaty连接目标资源服务器。
103 |
104 | > 如果是token是padlocal类型,则在python-wechaty程序内部可直接设置`export endpoint=localhost:port`来连接Gateway Server。
105 |
106 | 当然,以上的写法是使用Bash的方式来设置环境变量,也是可以通过python代码来设置环境变量,详细可看:
107 |
108 | ```python
109 | import os
110 | os.environ['token'] = "1fe5f846-3cfb-401d-b20c-XXXXX"
111 | os.environ['endpoint'] = "127.0.0.1:9001"
112 | ```
113 |
114 | ### 3.3 机器人启动代码
115 |
116 | > talke is cheep, show you the code
117 |
118 | ```python
119 | import asyncio, os
120 | from typing import List, Optional, Union
121 |
122 | from wechaty_puppet import FileBox # type: ignore
123 |
124 | from wechaty import Wechaty, Contact
125 | from wechaty.user import Message, Room
126 |
127 |
128 | class MyBot(Wechaty):
129 |
130 | async def on_message(self, msg: Message):
131 | """
132 | listen for message event
133 | """
134 | from_contact: Optional[Contact] = msg.talker()
135 | text = msg.text()
136 | room: Optional[Room] = msg.room()
137 | if text == 'ding':
138 | conversation: Union[
139 | Room, Contact] = from_contact if room is None else room
140 | await conversation.ready()
141 | await conversation.say('dong')
142 | file_box = FileBox.from_url(
143 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/'
144 | 'u=1116676390,2305043183&fm=26&gp=0.jpg',
145 | name='ding-dong.jpg')
146 | await conversation.say(file_box)
147 |
148 | os.environ['TOKEN'] = "1fe5f846-3cfb-401d-b20c-XXXXX"
149 | os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT'] = "127.0.0.1:9001"
150 | asyncio.run(MyBot().start())
151 | ```
152 | ### 运行代码
153 |
154 |
155 | 欢迎各位品尝以上代码 🥳
156 |
157 | * **相关链接**
158 | * [python-wechaty](https://github.com/wechaty/python-wechaty)
159 | * [python-wechaty-getting-started](https://github.com/wechaty/python-wechaty-getting-started)
--------------------------------------------------------------------------------
/python-wechaty/docs/tutorials/use_paimon_getting_started.md:
--------------------------------------------------------------------------------
1 | # 使用Paimon协议
2 |
3 | ## 一、介绍
4 |
5 | python原生支持paimon协议,不需要Token Gateway,简单方便。
6 | [免费申请7天试用Token](https://wechaty.js.org/docs/puppet-services/paimon)
7 |
8 |
9 | ## 二、连接服务
10 |
11 | ### 2.1 本地测试和远端部署
12 |
13 |
14 | ```shell
15 | export WECHATY_PUPPET_SERVICE_TOKEN=puppet_paimon_XXXXX
16 | # or
17 | export TOKEN=puppet_paimon_XXXXX
18 | # or
19 | export token=puppet_paimon_XXXXX
20 | ```
21 |
22 |
23 |
24 | 当然,以上的写法是使用Bash的方式来设置环境变量,也是可以通过python代码来设置环境变量,详细可看:
25 |
26 | ```python
27 | import os
28 | os.environ['token'] = "puppet_paimon_XXXXX"
29 | ```
30 |
31 | ## 三、示例代码
32 |
33 | > talke is cheep, show you the code
34 |
35 | ```python
36 | import asyncio, os
37 | from typing import List, Optional, Union
38 |
39 | from wechaty_puppet import FileBox # type: ignore
40 |
41 | from wechaty import Wechaty, Contact
42 | from wechaty.user import Message, Room
43 |
44 |
45 | class MyBot(Wechaty):
46 |
47 | async def on_message(self, msg: Message):
48 | """
49 | listen for message event
50 | """
51 | from_contact: Optional[Contact] = msg.talker()
52 | text = msg.text()
53 | room: Optional[Room] = msg.room()
54 | if text == 'ding':
55 | conversation: Union[
56 | Room, Contact] = from_contact if room is None else room
57 | await conversation.ready()
58 | await conversation.say('dong')
59 | file_box = FileBox.from_url(
60 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/'
61 | 'u=1116676390,2305043183&fm=26&gp=0.jpg',
62 | name='ding-dong.jpg')
63 | await conversation.say(file_box)
64 |
65 | os.environ['TOKEN'] = "1fe5f846-3cfb-401d-b20c-XXXXX"
66 | asyncio.run(MyBot().start())
67 | ```
68 |
69 | 欢迎各位品尝以上代码 🥳
70 |
71 | * **相关链接**
72 | * [python-wechaty](https://github.com/wechaty/python-wechaty)
73 | * [python-wechaty-getting-started](https://github.com/wechaty/python-wechaty-getting-started)
74 |
--------------------------------------------------------------------------------
/python-wechaty/docs/tutorials/use_web_getting_started.md:
--------------------------------------------------------------------------------
1 | # 使用免费Web协议
2 |
3 | ## 一、介绍
4 |
5 | 底层的对接实现是基于TypeScript语言,故无法直接在python-wechaty中使用该服务。可是Wechaty社区能够直接将其转化成对应的服务让多语言调用,从而实现:底层复用的特性。
6 |
7 | 整体步骤分为两步:
8 |
9 | * 使用Docker启动web协议服务
10 | * 使用python-wechaty连接服务
11 |
12 | ## 二、启动Web协议服务
13 |
14 | ```shell
15 | docker pull wechaty/wechaty:0.65
16 |
17 | export WECHATY_LOG="verbose"
18 | export WECHATY_PUPPET="wechaty-puppet-wechat"
19 | export WECHATY_PUPPET_SERVER_PORT="8080"
20 | export WECHATY_TOKEN="python-wechaty-{uuid}"
21 | export WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER="true"
22 |
23 | # save login session
24 | if [ ! -f "${WECHATY_TOKEN}.memory-card.json" ]; then
25 | touch "${WECHATY_TOKEN}.memory-card.json"
26 | fi
27 |
28 | docker run -ti \
29 | --name wechaty_puppet_service_token_gateway \
30 | --rm \
31 | -v "`pwd`/${WECHATY_TOKEN}.memory-card.json":"/wechaty/${WECHATY_TOKEN}.memory-card.json" \
32 | -e WECHATY_LOG \
33 | -e WECHATY_PUPPET \
34 | -e WECHATY_PUPPET_SERVER_PORT \
35 | -e WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER \
36 | -e WECHATY_TOKEN \
37 | -p "$WECHATY_PUPPET_SERVER_PORT:$WECHATY_PUPPET_SERVER_PORT" \
38 | wechaty/wechaty:0.65
39 | ```
40 |
41 | 注意:
42 |
43 | * WECHATY_TOKEN 必须使用生成的UUID来替换,不然直接使用该token来启动的服务很容易被他人盗窃。
44 |
45 | 小伙伴们可在python解释器中运行以下代码来获得随机TOKEN:
46 | ```python
47 | # 例如:b2ff8fc5-c5a2-4384-b317-3695807e483f
48 | import uuid;print(uuid.uuid4());
49 | ```
50 |
51 | ## 三、连接服务
52 |
53 | 当使用docker来启动web服务时,可分为在本地环境测试以及在远端环境中测试,在连接方式上有一些不一样。
54 |
55 | ### 3.1 本地WEB服务
56 |
57 | 当在计算机本地启动web服务后,可直接使用python-wechaty连接本地的服务,不通过token来获取对应的服务连接地址。示例代码如下:
58 |
59 | ```shell
60 | export WECHATY_PUPPET_SERVICE_ENDPOINT=127.0.0.1:8080
61 | ```
62 |
63 | 或者
64 |
65 | ```python
66 | import os
67 | os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT'] = '127.0.0.1:8080'
68 | ```
69 |
70 | > 当你的服务和python-wechaty机器人代码都部署在服务器中时,此时也属于本地服务,可使用此方法来配置。
71 |
72 | ### 3.2 远端服务
73 |
74 | 当把服务部署在远端服务器中时,要保证该计算机能够被外网访问到,且对应端口开放。例如在上述示例脚本中,比如保证服务器的`8080`端口开放,而你的服务器IP为:`10.12.123.23`,此时可直接设置服务连接地址:
75 |
76 | ```shell
77 | export WECHATY_PUPPET_SERVICE_ENDPOINT=10.12.123.23:8080
78 | ```
79 |
80 | 或者
81 |
82 | ```python
83 | import os
84 | os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT'] = '10.12.123.23:8080'
85 | ```
86 |
87 | ## 四、编写代码
88 |
89 | > talk is cheep, show you the code
90 |
91 | ```python
92 | import asyncio
93 | from typing import List, Optional, Union
94 |
95 | from wechaty_puppet import FileBox # type: ignore
96 |
97 | from wechaty import Wechaty, Contact
98 | from wechaty.user import Message, Room
99 |
100 |
101 | class MyBot(Wechaty):
102 |
103 | async def on_message(self, msg: Message):
104 | """
105 | listen for message event
106 | """
107 | from_contact: Optional[Contact] = msg.talker()
108 | text = msg.text()
109 | room: Optional[Room] = msg.room()
110 | if text == 'ding':
111 | conversation: Union[
112 | Room, Contact] = from_contact if room is None else room
113 | await conversation.ready()
114 | await conversation.say('dong')
115 | file_box = FileBox.from_url(
116 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/'
117 | 'u=1116676390,2305043183&fm=26&gp=0.jpg',
118 | name='ding-dong.jpg')
119 | await conversation.say(file_box)
120 |
121 | asyncio.run(MyBot().start())
122 | ```
123 |
124 | 欢迎各位品尝以上代码 🥳
125 |
--------------------------------------------------------------------------------
/python-wechaty/docs/tutorials/videos.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: 介绍视频
3 | summary: .
4 | authors:
5 | - wj-Mcat
6 | date: 2021-04-25
7 | some_url: https://github.com/wj-Mcat
8 | ---
9 |
10 | ## live-coding
11 |
12 | > 原始视频链接:[@吴京京 python-wechaty-live-coding](https://www.bilibili.com/video/BV1dv411k75G?from=search&seid=13874361539953942172)
13 |
14 |
15 |
16 | ## AI情话
17 |
18 | > 原始视频链接:[@Val_傲娇的鸽子 手把手教你做个用AI续写情话的Wechaty聊天机器人](https://www.bilibili.com/video/BV1BB4y1A714?from=search&seid=13874361539953942172)
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/python-wechaty/examples/contact-bot.py:
--------------------------------------------------------------------------------
1 | """doc"""
2 | import asyncio
3 | from typing import Optional
4 |
5 | from wechaty_puppet import ContactType
6 | from wechaty_puppet.logger import get_logger
7 |
8 | from wechaty import Wechaty, Contact
9 | from wechaty.utils.qrcode_terminal import qr_terminal_str
10 |
11 | log = get_logger('ContactBot')
12 |
13 | WELCOME_MSG = '''
14 | =============== Powered by Wechaty ===============
15 | -------- https://github.com/Chatie/wechaty --------
16 | Hello,
17 | I'm a Wechaty Botie with the following super powers:
18 | 1. List all your contacts with weixn id & name
19 | 2. Dump the avatars of your first 17 contacts
20 | __________________________________________________
21 | Hope you like it, and you are very welcome to
22 | upgrade me for more super powers!
23 | Please wait... I'm trying to login in...
24 | '''
25 |
26 | # pylint: disable=W0602
27 | bot: Optional[Wechaty] = None
28 | MAX_CONTACTS = 17
29 | INTERVAL = 7
30 |
31 |
32 | async def display_contacts() -> None:
33 | """Display all the contacts and dump the avatars"""
34 | # pylint: disable=W0603
35 | global bot
36 | assert bot is not None
37 | while True:
38 | contacts = await bot.Contact.find_all()
39 |
40 | log.info('#######################')
41 | log.info('Contact number: %s\n', len(contacts))
42 |
43 | for index, contact in enumerate(contacts):
44 | if contact.type() == ContactType.CONTACT_TYPE_PERSONAL:
45 | log.info('personal %s: %s : %s', index, contact.name, contact.get_id())
46 |
47 | for contact in contacts[:MAX_CONTACTS]:
48 | file = await contact.avatar()
49 | name = file.name
50 | await file.to_file(name, True)
51 |
52 | log.info('Contact: "%s" with avatar file: "%s"', contact.name, name)
53 |
54 | if len(contacts) > MAX_CONTACTS:
55 | log.info('Too many contacts. I only show you the first %s ones...', MAX_CONTACTS)
56 |
57 | log.info('I will re-dump contact weixin id & names after %s second... ', INTERVAL)
58 | await asyncio.sleep(INTERVAL)
59 |
60 |
61 | async def handle_login(user: Contact) -> None:
62 | """Handle the login event"""
63 | log.info('%s logged in', user.name)
64 | await user.say('wechaty contact-bot just logged in')
65 | await display_contacts()
66 |
67 |
68 | async def main() -> None:
69 | """The main function for the contact-bot module"""
70 | # pylint: disable=W0603
71 | global bot
72 | print(WELCOME_MSG)
73 | bot = Wechaty()\
74 | .on('login', handle_login)\
75 | .on('error', lambda error: log.info('error: %s', error))\
76 | .on('scan',
77 | lambda qrcode, status: print(f'{qr_terminal_str(qrcode)}\n'
78 | f'[{status}] Scan QR Code in above url to login:'))
79 | await bot.start()
80 |
81 |
82 | asyncio.run(main())
83 |
--------------------------------------------------------------------------------
/python-wechaty/examples/ding-dong-bot.py:
--------------------------------------------------------------------------------
1 | """doc"""
2 | import asyncio
3 | import logging
4 | from typing import Optional, Union
5 |
6 | from wechaty_puppet import FileBox
7 |
8 | from wechaty import Wechaty, Contact
9 | from wechaty.user import Message, Room
10 |
11 | logging.basicConfig(
12 | level=logging.INFO,
13 | format='%(asctime)s %(levelname)s %(filename)s <%(funcName)s> %(message)s',
14 | datefmt='%Y-%m-%d %H:%M:%S',
15 | )
16 |
17 | log = logging.getLogger(__name__)
18 |
19 |
20 | async def message(msg: Message) -> None:
21 | """back on message"""
22 | from_contact = msg.talker()
23 | text = msg.text()
24 | room = msg.room()
25 | if text == 'ding':
26 | conversation: Union[
27 | Room, Contact] = from_contact if room is None else room
28 | await conversation.ready()
29 | await conversation.say('dong')
30 | file_box = FileBox.from_url(
31 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/'
32 | 'u=1116676390,2305043183&fm=26&gp=0.jpg',
33 | name='ding-dong.jpg')
34 | await conversation.say(file_box)
35 |
36 | bot: Optional[Wechaty] = None
37 |
38 |
39 | async def main() -> None:
40 | """doc"""
41 | # pylint: disable=W0603
42 | global bot
43 | bot = Wechaty().on('message', message)
44 | await bot.start()
45 |
46 |
47 | asyncio.run(main())
48 |
--------------------------------------------------------------------------------
/python-wechaty/examples/health_check_plugin.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/python-wechaty/examples/health_check_plugin.py
--------------------------------------------------------------------------------
/python-wechaty/examples/plugin-server-bot.py:
--------------------------------------------------------------------------------
1 | """doc"""
2 | import asyncio
3 | import logging
4 | from typing import Optional, Union
5 | from quart import Quart
6 |
7 | from wechaty_puppet import FileBox, PuppetOptions
8 |
9 | from wechaty import Wechaty, Contact, WechatyPlugin, WechatyOptions
10 | from wechaty.user import Message, Room
11 |
12 | logging.basicConfig(
13 | level=logging.INFO,
14 | format='%(asctime)s %(levelname)s %(filename)s <%(funcName)s> %(message)s',
15 | datefmt='%Y-%m-%d %H:%M:%S',
16 | )
17 |
18 | log = logging.getLogger(__name__)
19 |
20 |
21 | class SimpleServerWechatyPlugin(WechatyPlugin):
22 | """
23 | simple hello wechaty web server plugin
24 | """
25 | async def blueprint(self, app: Quart) -> None:
26 | @app.route('/wechaty')
27 | def hello_wechaty() -> str:
28 | """helo blueprint function"""
29 | return 'hello wechaty'
30 |
31 |
32 | async def message(msg: Message) -> None:
33 | """back on message"""
34 | from_contact = msg.talker()
35 | text = msg.text()
36 | room = msg.room()
37 | if text == '#ding':
38 | conversation: Union[
39 | Room, Contact] = from_contact if room is None else room
40 | await conversation.ready()
41 | await conversation.say('dong')
42 | file_box = FileBox.from_url(
43 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/'
44 | 'u=1116676390,2305043183&fm=26&gp=0.jpg',
45 | name='ding-dong.jpg')
46 | await conversation.say(file_box)
47 |
48 | bot: Optional[Wechaty] = None
49 |
50 |
51 | async def main() -> None:
52 | """doc"""
53 | # pylint: disable=W0603
54 | global bot
55 | options = WechatyOptions(
56 | host='127.0.0.1',
57 | port=5005,
58 | puppet_options=PuppetOptions(
59 | token='your-token'
60 | )
61 | )
62 |
63 | bot = Wechaty(
64 | options=options
65 | ).on('message', message)
66 | bot.use(SimpleServerWechatyPlugin())
67 |
68 | await bot.start()
69 |
70 |
71 | asyncio.run(main())
72 |
--------------------------------------------------------------------------------
/python-wechaty/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: python-wechaty
2 | repo_url: http://github.com/wechaty/python-wechaty
3 | repo_name: GitHub
4 | site_description: 'python-wechaty AI Chatbot'
5 | copyright: '@wechaty community'
6 | nav:
7 | - 介绍: 'index.md'
8 | - 快速开始:
9 | - 介绍: 'tutorials/index.md'
10 | - 使用Padlocal协议快速开始一个微信机器人: 'tutorials/use_padlocal_getting_started.md'
11 | - 使用免费web协议快速开始一个微信机器人: 'tutorials/use_web_getting_started.md'
12 | - 使用paimon协议快速开始一个微信机器人: 'tutorials/use_paimon_getting_started.md'
13 | - 视频教程: 'tutorials/videos.md'
14 | - 使用指南:
15 | - 介绍: 'how-to/how-to_introduction.md'
16 | - 如何添加好友: 'how-to/how-to_add_friendship.md'
17 | - 如何关键字入群: 'how-to/how-to_room_inviter.md'
18 | - 如何完成自动回复: 'how-to/how-to_auto_reply.md'
19 | - 如何检索群聊或联系人: 'how-to/how-to_finder.md'
20 | - 如何完成任务调度: 'how-to/how-to_scheduler.md'
21 | - 如何完成群消息同步: 'how-to/how-to_message_forward.md'
22 | - 如何使用Rasa Sever: 'how-to/how-to_rasa.md'
23 | - 如何使用Github Webhook插件: 'how-to/how-to_github_webhook.md'
24 | - 如何使用Gitlab Webhook插件: 'how-to/how-to_gitlab_webhook.md'
25 | - 如何使用插件系统: 'how-to/how-to_use_plugin.md'
26 | - 模块详解:
27 | - 介绍: 'references/index.md'
28 | - 'Wechaty模块': 'references/wechaty.md'
29 | - '消息模块': 'references/message.md'
30 | - '联系人模块': 'references/contact.md'
31 | - '登录人模块': 'references/contact-self.md'
32 | - '好友关系模块': 'references/friendship.md'
33 | - '群聊模块': 'references/room.md'
34 | - '群聊邀请模块': 'references/room-invitation.md'
35 | - 'filebox模块': 'references/filebox.md'
36 | - API文档:
37 | - 'wechaty.Wechaty': 'api/wechaty.md'
38 | - 'wechaty.accessory': 'api/accessory.md'
39 | - 'wechaty.config': 'api/config.md'
40 | - 'wechaty.types': 'api/types.md'
41 | - 'wechaty.plugin': 'api/plugin.md'
42 | - wechaty.utils:
43 | - 'wechaty.utils.date_util': 'api/utils/date_util.md'
44 | - 'wechaty.utils.async_helper': 'api/utils/async_helper.md'
45 | - 'wechaty.utils.link': 'api/utils/link.md'
46 | - 'wechaty.utils.qr_code': 'api/utils/qr_code.md'
47 | - 'wechaty.utils.qrcode_teminal': 'api/utils/qrcode_terminal.md'
48 | - 'wechaty.utils.type_check': 'api/utils/type_check.md'
49 | - wechaty.user:
50 | - 'wechaty.user.contact': 'api/user/contact.md'
51 | - 'wechaty.user.contact_self': 'api/user/contact_self.md'
52 | - 'wechaty.user.favorite': 'api/user/favorite.md'
53 | - 'wechaty.user.friendship': 'api/user/friendship.md'
54 | - 'wechaty.user.image': 'api/user/image.md'
55 | - 'wechaty.user.message': 'api/user/message.md'
56 | - 'wechaty.user.mini_program': 'api/user/mini_program.md'
57 | - 'wechaty.user.room': 'api/user/room.md'
58 | - 'wechaty.user.room_invitation': 'api/user/room_invitation.md'
59 | - 'wechaty.user.tag': 'api/user/tag.md'
60 | - 'wechaty.user.url_link': 'api/user/url_link.md'
61 | - 设计理念:
62 | - 介绍: 'explanation/index.md'
63 | - 不同协议比较: 'explanation/different_protocol.md'
64 | - 插件系统: 'explanation/why_plugin.md'
65 | - FAQ:
66 | - '基础常见问题': 'faq/common.md'
67 | - '什么是Puppet': 'faq/what-is-a-puppet.md'
68 |
69 | theme:
70 | name: material
71 | logo: img/wechaty-icon-white.svg
72 | favicon: img/favicon.ico
73 |
74 | markdown_extensions:
75 | - pymdownx.highlight
76 | - pymdownx.superfences
77 | - toc:
78 | baselevel: 2
79 |
80 | google_analytics:
81 | - G-1TDFTF2BYD
82 | - auto
83 |
84 | plugins:
85 | - search
86 | - mkdocstrings:
87 | handlers:
88 | python:
89 | selection:
90 | filters:
91 | - "!^_" # exlude all members starting with _
92 | - "^__init__$" # but always include __init__ modules and methods
93 | rendering:
94 | show_root_heading: yes
95 | show_root_full_path: false
96 | members_order: source
97 | heading_level: 2
98 | watch:
99 | - ./src
--------------------------------------------------------------------------------
/python-wechaty/pyproject.toml:
--------------------------------------------------------------------------------
1 | [tool.mypy]
2 | disallow_untyped_defs = true
3 | warn_unused_ignores = true
4 | ignore_missing_imports = false
5 |
6 | [[tool.mypy.overrides]]
7 | module = [
8 | "qrcode.*",
9 | "wechaty_puppet.*",
10 | "pytest.*",
11 | "grpclib.*",
12 | "lxml.*",
13 | "apscheduler.*",
14 | "pyee.*"
15 | ]
16 | ignore_missing_imports = true
17 |
18 | # refer to: https://docs.pytest.org/en/stable/mark.html
19 | [tool.pytest.ini_options]
20 | minversion = "6.0"
21 | addopts = "--cov-report term-missing --cov-report xml --cov=src/"
22 | testpaths = [
23 | "tests",
24 | ]
25 | markers = [
26 | "asyncio"
27 | ]
28 | pythonpath='./src'
--------------------------------------------------------------------------------
/python-wechaty/requirements-dev.txt:
--------------------------------------------------------------------------------
1 | # use the latest version of setuptools
2 | setuptools>=63.2.0
3 | flake8
4 | mypy
5 | mypy_extensions
6 | pycodestyle
7 | pylint
8 | pylint-quotes
9 | pytest
10 |
11 | # TODO(wj-Mcat): find out why latest pytest-asyncio will make test failed
12 | pytest-asyncio==0.18.3
13 | pytest-cov
14 | pytype
15 | semver
16 | pyee
17 | requests
18 | qrcode
19 | apscheduler
20 | lxml
21 | pre-commit
22 | mkdocs
23 | mkdocs-material
24 | types-requests
25 | mkdocstrings
26 | mkdocstrings-python-legacy
27 | yapf
--------------------------------------------------------------------------------
/python-wechaty/requirements.txt:
--------------------------------------------------------------------------------
1 | pyee
2 | requests
3 | qrcode
4 | lxml
5 | wechaty-puppet>=0.4.19
6 | wechaty-puppet-service>=0.8.9
7 | quart
8 | opengraph_py3
9 | Quart-CORS
10 | APScheduler
11 | SQLAlchemy
--------------------------------------------------------------------------------
/python-wechaty/scripts/check_python_version.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | """check version"""
3 |
4 | import re
5 | import sys
6 | from typing import Tuple
7 |
8 |
9 | def version() -> Tuple[int, int, int]:
10 | """version"""
11 | try:
12 | ver = re.findall(r'^\d+\.\d+\.\d+', sys.version)[0]
13 | senior, minor, patch = re.findall(r'\d+', ver)
14 | return (int(senior), int(minor), int(patch))
15 |
16 | # pylint: disable=W0703
17 | except Exception:
18 | return (0, 0, 0)
19 |
20 |
21 | # major, minor, patch = version()
22 |
23 |
24 | if sys.version_info < (3, 6):
25 | sys.exit('ERROR: Python 3.7 or above is required.')
26 | else:
27 | print('Python %d.%d.%d passed checking.' % sys.version_info[:3])
28 |
--------------------------------------------------------------------------------
/python-wechaty/setup.cfg:
--------------------------------------------------------------------------------
1 | # NOTE: All relative paths are relative to the location of this file.
2 |
3 | [pytype]
4 | # Space-separated list of files or directories to exclude.
5 | exclude =
6 | **/*_test.py
7 | **/test_*.py
8 |
9 | # Space-separated list of files or directories to process.
10 | inputs =
11 | .
12 |
13 | # Keep going past errors to analyze as many files as possible.
14 | keep_going = False
15 |
16 | # Run N jobs in parallel.
17 | jobs = 4
18 |
19 | # All pytype output goes here.
20 | output = .pytype
21 |
22 | # Paths to source code directories, separated by ':'.
23 | pythonpath =
24 | .
25 |
26 | # Python version (major.minor) of the target code.
27 | python_version = 3.7
28 |
29 | # Comma or space separated list of error names to ignore.
30 | disable =
31 | pyi-error
32 | # import-error
33 |
34 | # Don't report errors.
35 | report_errors = True
36 |
37 | # Experimental: Infer precise return types even for invalid function calls.
38 | precise_return = False
39 |
40 | # Experimental: solve unknown types to label with structural types.
41 | protocols = False
42 |
43 | # Experimental: Only load submodules that are explicitly imported.
44 | strict_import = False
45 |
--------------------------------------------------------------------------------
/python-wechaty/setup.py:
--------------------------------------------------------------------------------
1 | """
2 | setup
3 | """
4 | import os
5 | from typing import List
6 |
7 | import semver
8 | import setuptools
9 |
10 |
11 | def versioning(version: str) -> str:
12 | """
13 | version to specification
14 | Author: Huan (https://github.com/huan)
15 |
16 | X.Y.Z -> X.Y.devZ
17 |
18 | """
19 | sem_ver = semver.parse(version)
20 |
21 | major = sem_ver['major']
22 | minor = sem_ver['minor']
23 | patch = str(sem_ver['patch'])
24 |
25 | if minor % 2:
26 | patch = 'dev' + patch
27 |
28 | fin_ver = '%d.%d.%s' % (
29 | major,
30 | minor,
31 | patch,
32 | )
33 |
34 | return fin_ver
35 |
36 |
37 | def get_version() -> str:
38 | """
39 | read version from VERSION file
40 | """
41 | version = '0.0.0'
42 |
43 | with open(
44 | os.path.join(
45 | os.path.dirname(__file__),
46 | 'VERSION'
47 | )
48 | ) as version_fh:
49 | # Get X.Y.Z
50 | version = version_fh.read().strip()
51 | # versioning from X.Y.Z to X.Y.devZ
52 | version = versioning(version)
53 |
54 | return version
55 |
56 |
57 | def get_long_description() -> str:
58 | """get long_description"""
59 | with open('README.md', 'r') as readme_fh:
60 | return readme_fh.read()
61 |
62 |
63 | def get_install_requires() -> List[str]:
64 | """get install_requires"""
65 | with open('requirements.txt', 'r') as requirements_fh:
66 | return requirements_fh.read().splitlines()
67 |
68 |
69 | setuptools.setup(
70 | name='wechaty',
71 | version=get_version(),
72 | author='Jingjing WU (吴京京)',
73 | author_email='wechaty@chatie.io',
74 | description='Wechaty is a Conversational RPA SDK for Chatbot Makers',
75 | long_description=get_long_description(),
76 | long_description_content_type='text/markdown',
77 | license='Apache-2.0',
78 | url='https://github.com/wechaty/python-wechaty',
79 | packages=setuptools.find_packages('src'),
80 | package_dir={'wechaty': 'src/wechaty'},
81 | install_requires=get_install_requires(),
82 | classifiers=[
83 | 'Programming Language :: Python :: 3.7',
84 | 'License :: OSI Approved :: Apache Software License',
85 | 'Operating System :: OS Independent',
86 | ],
87 | )
88 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/__init__.py:
--------------------------------------------------------------------------------
1 | """doc"""
2 |
3 | #
4 | # import types from wechaty_puppet
5 | #
6 |
7 | from wechaty_puppet import (
8 | FileBox,
9 | MessageType,
10 | MessagePayload,
11 |
12 | # Contact
13 | ContactGender,
14 | ContactType,
15 | ContactPayload,
16 |
17 | # Friendship
18 | FriendshipType,
19 | FriendshipPayload,
20 |
21 | # Room
22 | RoomPayload,
23 | RoomMemberPayload,
24 |
25 | # UrlLink
26 |
27 | # RoomInvitation
28 | RoomInvitationPayload,
29 |
30 | # Image
31 | ImageType,
32 |
33 | # Event
34 | EventType,
35 | EventReadyPayload,
36 |
37 | RoomQueryFilter,
38 | RoomMemberQueryFilter,
39 | FriendshipSearchQueryFilter,
40 | ContactQueryFilter,
41 | MessageQueryFilter,
42 | ScanStatus
43 | )
44 |
45 | from .config import (
46 | get_logger,
47 | )
48 | from .accessory import Accessory
49 | from .plugin import (
50 | WechatyPlugin,
51 | WechatyPluginOptions
52 | )
53 | from .wechaty import (
54 | Wechaty,
55 | WechatyOptions,
56 | )
57 | from .user import (
58 | Contact,
59 | Favorite,
60 | Friendship,
61 | Image,
62 | Message,
63 | MiniProgram,
64 | Room,
65 | RoomInvitation,
66 | Tag,
67 | UrlLink,
68 | )
69 | from .exceptions import (
70 | WechatyError,
71 | WechatyConfigurationError,
72 | WechatyAccessoryBindingError,
73 | WechatyStatusError,
74 | WechatyPayloadError,
75 | WechatyOperationError,
76 | WechatyPluginError,
77 | )
78 |
79 | from .version import VERSION
80 |
81 | __version__ = VERSION
82 |
83 | __all__ = [
84 | 'Accessory',
85 | 'Contact',
86 | 'Favorite',
87 | 'FileBox',
88 | 'Friendship',
89 | 'get_logger',
90 | 'Image',
91 | 'Message',
92 | 'MiniProgram',
93 | 'Room',
94 | 'RoomInvitation',
95 | 'Tag',
96 | 'UrlLink',
97 | 'Wechaty',
98 | 'WechatyOptions',
99 |
100 | 'WechatyPlugin',
101 | 'WechatyPluginOptions',
102 |
103 | 'MessageType',
104 | 'MessagePayload',
105 |
106 | # Contact
107 | 'ContactGender',
108 | 'ContactType',
109 | 'ContactPayload',
110 |
111 | # Friendship
112 | 'FriendshipType',
113 | 'FriendshipPayload',
114 |
115 | # Room
116 | 'RoomPayload',
117 | 'RoomMemberPayload',
118 |
119 | # UrlLink
120 |
121 | # RoomInvitation
122 | 'RoomInvitationPayload',
123 |
124 | # Image
125 | 'ImageType',
126 |
127 | # Event
128 | 'EventType',
129 | 'EventReadyPayload',
130 |
131 | 'ScanStatus',
132 |
133 | 'RoomQueryFilter',
134 | 'RoomMemberQueryFilter',
135 | 'FriendshipSearchQueryFilter',
136 | 'ContactQueryFilter',
137 | 'MessageQueryFilter',
138 |
139 | # Error
140 | 'WechatyError',
141 | 'WechatyConfigurationError',
142 | 'WechatyAccessoryBindingError',
143 | 'WechatyStatusError',
144 | 'WechatyPayloadError',
145 | 'WechatyOperationError',
146 | 'WechatyPluginError',
147 | ]
148 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/accessory.py:
--------------------------------------------------------------------------------
1 | """
2 | Python Wechaty - https://github.com/wechaty/python-wechaty
3 |
4 | Authors: Huan LI (李卓桓)
5 | Jingjing WU (吴京京)
6 |
7 | 2018-now @copyright Wechaty
8 |
9 | Licensed under the Apache License, Version 2.0 (the "License");
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | http://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an "AS IS" BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | """
21 | from __future__ import annotations
22 |
23 | from typing import (
24 | TYPE_CHECKING,
25 | # overload,
26 | # cast,
27 | Optional,
28 | TypeVar,
29 | Generic,
30 | )
31 |
32 | from wechaty_puppet import (
33 | get_logger,
34 | Puppet,
35 | )
36 |
37 | from wechaty.exceptions import WechatyAccessoryBindingError
38 |
39 | # pylint:disable=R0401
40 | if TYPE_CHECKING:
41 | from .wechaty import Wechaty
42 |
43 | log = get_logger('Accessory')
44 |
45 | PayloadType = TypeVar('PayloadType')
46 |
47 |
48 | class Accessory(Generic[PayloadType]):
49 | """
50 | Translate the function from TypeScript to Python
51 | See: https://github.com/wechaty/wechaty/blob/master/src/accessory.ts
52 | """
53 |
54 | _puppet: Optional[Puppet] = None
55 | _wechaty: Optional[Wechaty] = None
56 |
57 | abstract: bool = True
58 |
59 | def __init__(self) -> None:
60 | if self.abstract:
61 | raise WechatyAccessoryBindingError(
62 | 'Do not instantiate class {cls} directly, sse with bot.{cls} instead. '
63 | 'See https://github.com/wechaty/wechaty/issues/1217'.format(
64 | cls=type(self).__name__
65 | )
66 | )
67 |
68 | self._payload: Optional[PayloadType] = None
69 |
70 | @property
71 | def payload(self) -> PayloadType:
72 | """
73 | get the payload object as a property
74 | :return:
75 | """
76 | if self._payload is None:
77 | raise ValueError(
78 | f'should ready() the {type(self).__name__} payload before get it, '
79 | 'please call the method'
80 | )
81 | return self._payload
82 |
83 | @payload.setter
84 | def payload(self, value: PayloadType) -> None:
85 | """
86 | :param value:
87 | :return:
88 | """
89 | if self._payload:
90 | log.warning('<%s> set payload more than once', self)
91 | self._payload = value
92 |
93 | def is_ready(self) -> bool:
94 | """
95 | check if payload is ready
96 | :return:
97 | """
98 | return self._puppet is not None and self._payload is not None
99 |
100 | @classmethod
101 | def set_puppet(cls, new_puppet: Puppet) -> None:
102 | """doc"""
103 | if cls._puppet is not None:
104 | raise AttributeError('can not set _puppet twice')
105 | cls._puppet = new_puppet
106 |
107 | @classmethod
108 | def set_wechaty(cls, new_wechaty: Wechaty) -> None:
109 | """doc"""
110 | if cls._wechaty is not None:
111 | raise AttributeError('can not set _wechaty twice')
112 | cls._wechaty = new_wechaty
113 |
114 | @classmethod
115 | def get_puppet(cls) -> Puppet:
116 | """doc"""
117 | if cls._puppet is None:
118 | raise AttributeError('puppet not found')
119 | return cls._puppet
120 |
121 | @classmethod
122 | def get_wechaty(cls) -> Wechaty:
123 | """doc"""
124 | if cls._wechaty is None:
125 | raise AttributeError('wechaty not found')
126 | return cls._wechaty
127 |
128 | @property
129 | def puppet(self) -> Puppet:
130 | """doc"""
131 | if self._puppet is None:
132 | raise AttributeError('puppet not set')
133 | return self._puppet
134 |
135 | @property
136 | def wechaty(self) -> Wechaty:
137 | """
138 | instance property
139 | """
140 | if self._wechaty is None:
141 | raise AttributeError('wechaty not set')
142 | return self._wechaty
143 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/config.py:
--------------------------------------------------------------------------------
1 | """
2 | Python Wechaty - https://github.com/wechaty/python-wechaty
3 |
4 | Authors: Huan LI (李卓桓)
5 | Jingjing WU (吴京京)
6 |
7 | 2020-now @ Copyright Wechaty
8 |
9 | Licensed under the Apache License, Version 2.0 (the 'License');
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | http://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an 'AS IS' BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | """
21 | import os
22 | import re
23 | from typing import (
24 | Optional,
25 | )
26 |
27 | from wechaty_puppet import (
28 | FileBox,
29 | get_logger
30 | )
31 |
32 |
33 | log = get_logger('Config')
34 |
35 | # log.debug('test logging debug')
36 | # log.info('test logging info')
37 |
38 |
39 | # TODO(wj-Mcat): there is no reference usage, so need to be removed
40 | _FILE_PATH = os.path.dirname(os.path.realpath(__file__))
41 | DATA_PATH = os.path.realpath(
42 | os.path.join(
43 | _FILE_PATH,
44 | '../data',
45 | ),
46 | )
47 |
48 | # http://jkorpela.fi/chars/spaces.html
49 | # String.fromCharCode(8197)
50 | AT_SEPARATOR = chr(0x2005)
51 |
52 | # refer to:https://github.com/wechaty/python-wechaty/issues/285#issuecomment-997441596
53 | PARALLEL_TASK_NUM = 100
54 |
55 |
56 | def global_exception_handler(exception: Exception) -> None:
57 | """
58 | handle the global exception
59 | :param exception: exception message
60 | :return:
61 | """
62 | log.error('occur %s %s', exception.__class__.__name__, str(exception.args))
63 | print(exception)
64 |
65 |
66 | class DefaultSetting(dict):
67 | """
68 | store global default setting
69 | """
70 | default_api_host: Optional[str] = None
71 | default_port: Optional[int] = None
72 | default_protocol: Optional[str] = None
73 |
74 |
75 | # pylint: disable=R0903
76 | def valid_api_host(api_host: str) -> bool:
77 | """
78 | test the validation of the api_host
79 | :param api_host:
80 | :return:
81 | """
82 | pattern = re.compile(
83 | r'^(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|:?[0-9]*'
84 | r'([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|:?[0-9]*'
85 | r'([a-zA-Z0-9][-_.a-zA-Z0-9]{0,61}[a-zA-Z0-9]))\.:?[0-9]*'
86 | r'([a-zA-Z]{2,13}|[a-zA-Z0-9-]{2,30}.[a-zA-Z]{2,3}):?[0-9]*$'
87 | )
88 | return bool(pattern.match(api_host))
89 |
90 |
91 | class Config:
92 | """
93 | get the configuration from the environment variables
94 | """
95 | def __init__(self) -> None:
96 | self._cache_dir: Optional[str] = None
97 |
98 | @property
99 | def cache_dir(self) -> str:
100 | """get the cache dir in the lazy loading mode
101 |
102 | Returns:
103 | str: the path of cache dir
104 | """
105 | if self._cache_dir is not None:
106 | return self._cache_dir
107 | self._cache_dir = os.environ.get("CACHE_DIR", '.wechaty')
108 | assert self._cache_dir is not None
109 | return self._cache_dir
110 |
111 |
112 | # export const CHATIE_OFFICIAL_ACCOUNT_ID = 'gh_051c89260e5d'
113 | # chatie_official_account_id = 'gh_051c89260e5d'
114 | # CHATIE_OFFICIAL_ACCOUNT_ID = 'gh_051c89260e5d'
115 |
116 |
117 | def qr_code_for_chatie() -> FileBox:
118 | """
119 | create QRcode for chatie
120 | :return:
121 | """
122 | # const CHATIE_OFFICIAL_ACCOUNT_QRCODE =
123 | # 'http://weixin.qq.com/r/qymXj7DEO_1ErfTs93y5'
124 | chatie_official_account_qr_code: str = \
125 | 'http://weixin.qq.com/r/qymXj7DEO_1ErfTs93y5'
126 | return FileBox.from_qr_code(chatie_official_account_qr_code)
127 |
128 |
129 | config = Config()
130 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/exceptions.py:
--------------------------------------------------------------------------------
1 | """
2 | Python Wechaty - https://github.com/wechaty/python-wechaty
3 |
4 | Authors: Alfred Huang (黃文超)
5 |
6 | 2020-now @ Copyright Wechaty
7 |
8 | Licensed under the Apache License, Version 2.0 (the 'License');
9 | you may not use this file except in compliance with the License.
10 | You may obtain a copy of the License at
11 |
12 | http://www.apache.org/licenses/LICENSE-2.0
13 |
14 | Unless required by applicable law or agreed to in writing, software
15 | distributed under the License is distributed on an 'AS IS' BASIS,
16 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 | See the License for the specific language governing permissions and
18 | limitations under the License.
19 | """
20 |
21 |
22 | from typing import Any
23 |
24 |
25 | class WechatyError(Exception):
26 | """ Wechaty error """
27 |
28 | def __init__(self, message: str, code: Any = None, params: Any = None):
29 | super().__init__(message, code, params)
30 |
31 | self.message = message
32 | self.code = code
33 | self.params = params
34 |
35 | def __str__(self) -> str:
36 | return repr(self)
37 |
38 |
39 | class WechatyAccessoryBindingError(WechatyError):
40 | """ Raises when using Accessory classes in the wrong way """
41 |
42 |
43 | class WechatyStatusError(WechatyError, AttributeError):
44 | """ Wechaty method calling o non-proper status (e.g. lack of await ready) """
45 |
46 |
47 | class WechatyConfigurationError(WechatyError, AttributeError):
48 | """ Raises when configuration out of expected case """
49 |
50 |
51 | class WechatyOperationError(WechatyError):
52 | """ Logical out of business error occurs when using wechaty """
53 |
54 |
55 | class WechatyPluginError(WechatyError):
56 | """ Error occurs when using plugin """
57 |
58 |
59 | class WechatyPayloadError(WechatyError, ValueError):
60 | """ Error occurs when the GRPC service return data out of expected """
61 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/py.typed:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/python-wechaty/src/wechaty/py.typed
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/types.py:
--------------------------------------------------------------------------------
1 | """
2 | Python Wechaty - https://github.com/wechaty/python-wechaty
3 |
4 | Authors: Huan LI (李卓桓)
5 | Jingjing WU (吴京京)
6 |
7 | 2020-now @ Copyright Wechaty
8 |
9 | Licensed under the Apache License, Version 2.0 (the 'License');
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | http://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an 'AS IS' BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | """
21 | from __future__ import annotations
22 |
23 | from typing import (
24 | List,
25 | Optional,
26 | Union,
27 | TYPE_CHECKING,
28 | )
29 | from abc import ABC
30 |
31 | if TYPE_CHECKING:
32 | from .user import (
33 | Message,
34 | Contact,
35 | )
36 |
37 |
38 | # pylint: disable=R0903
39 | class Sayable(ABC):
40 | """
41 | wechaty sayable interface
42 | """
43 | async def say(
44 | self, text: str,
45 | reply_to: Union[Contact, List[Contact]]
46 | ) -> Optional[Message]:
47 | """
48 | derived classes must implement this function
49 | """
50 | raise NotImplementedError
51 |
52 |
53 | # pylint: disable=R0903
54 | class Acceptable(ABC):
55 | """
56 | wechaty acceptable interface
57 | """
58 | async def accept(self) -> None:
59 | """
60 | derived classes must implement this function
61 | """
62 | raise NotImplementedError
63 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/user/__init__.py:
--------------------------------------------------------------------------------
1 | """doc"""
2 | from __future__ import annotations
3 |
4 | from .contact import Contact
5 | from .favorite import Favorite
6 | from .friendship import Friendship
7 | from .image import Image
8 | from .message import Message
9 | from .mini_program import MiniProgram
10 | from .room import Room
11 | from .tag import Tag
12 | from .url_link import UrlLink
13 | from .room_invitation import RoomInvitation
14 | from .contact_self import ContactSelf
15 |
16 | # Huan(202003): is that necessary to put "name" to `__all__`?
17 | # name = 'user'
18 |
19 | __all__ = [
20 | 'Contact',
21 | 'Favorite',
22 | 'Friendship',
23 | 'Image',
24 | 'Message',
25 | 'MiniProgram',
26 | 'Room',
27 | 'Tag',
28 | 'UrlLink',
29 | 'RoomInvitation',
30 | 'ContactSelf'
31 | ]
32 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/user/contact_self.py:
--------------------------------------------------------------------------------
1 | """ContactSelf"""
2 |
3 | from __future__ import annotations
4 | from typing import Any, Optional, Type
5 |
6 | from wechaty import FileBox, get_logger
7 | from wechaty.exceptions import WechatyOperationError
8 | from wechaty.user.contact import Contact
9 |
10 | log = get_logger('ContactSelf')
11 |
12 |
13 | class ContactSelf(Contact):
14 | """ContactSelf"""
15 |
16 | async def avatar(self, file_box: Optional[FileBox] = None) -> FileBox:
17 | """
18 | Get or set avatar of ContactSelf.
19 |
20 | Args:
21 | file_box: FileBox object, if not provided, it will return a FileBox object
22 |
23 | Examples:
24 | >>> contact_self = bot.contact_self()
25 | >>> file_box = await contact_self.avatar()
26 | >>> file_box = await contact_self.avatar(file_box)
27 |
28 | Raises:
29 | WechatyOperationError: if the contact is not self, it will not get the avatar
30 |
31 | Returns:
32 | FileBox: file_box
33 | """
34 | log.info('avatar(%s)' % file_box.name if file_box else '')
35 | if not file_box:
36 | file_box = await super().avatar(None)
37 | return file_box
38 |
39 | if self.contact_id != self.puppet.self_id():
40 | raise WechatyOperationError('set avatar only available for user self')
41 |
42 | await self.puppet.contact_avatar(self.contact_id, file_box)
43 |
44 | async def qr_code(self) -> str:
45 | """
46 | Return the qrcode of ContactSelf
47 |
48 | Examples:
49 | >>> contact_self = bot.contact_self()
50 | >>> qr_code = await contact_self.qr_code()
51 |
52 | Raises:
53 | WechatyOperationError: if there is login exception, it will not get the qrcode
54 | WechatyOperationError: if the contact is not self, it will not get the qrcode
55 |
56 | Returns:
57 | str: the content of qrcode
58 | """
59 | try:
60 | contact_id: str = self.puppet.self_id()
61 | except Exception:
62 | raise WechatyOperationError(
63 | 'Can not get qr_code, user might be either not logged in or already logged out'
64 | )
65 |
66 | if self.contact_id != contact_id:
67 | raise WechatyOperationError('only can get qr_code for the login user self')
68 | qr_code_value = await self.puppet.contact_self_qr_code()
69 | return qr_code_value
70 |
71 | @property
72 | def name(self) -> str:
73 | """
74 | Get the name of login contact.
75 |
76 | Examples:
77 | >>> contact_self = bot.contact_self()
78 | >>> name = contact_self.name
79 |
80 | Returns:
81 | str: the name of Login User
82 | """
83 | return super().name
84 |
85 | async def set_name(self, name: str) -> None:
86 | """
87 | Set the name of login contact.
88 |
89 | Args:
90 | name: new name
91 |
92 | Examples:
93 | >>> contact_self = bot.contact_self()
94 | >>> await contact_self.set_name('new name')
95 | """
96 | await self.puppet.contact_self_name(name)
97 | await self.ready(force_sync=True)
98 |
99 | async def signature(self, signature: str) -> Any:
100 | """
101 | Set the signature of login contact.
102 |
103 | Args:
104 | signature: new signature
105 |
106 | Examples:
107 | >>> contact_self = bot.contact_self()
108 | >>> await contact_self.signature('new signature')
109 |
110 | Raises:
111 | WechatyOperationError: if there is login exception, it will not set the signature
112 | WechatyOperationError: if the contact is not self, it will not set the signature
113 |
114 | Returns:
115 | Any: the signature of login user
116 | """
117 | puppet_id = self.puppet.self_id()
118 |
119 | if self.contact_id != puppet_id:
120 | raise WechatyOperationError('only can get qr_code for the login user self')
121 |
122 | return self.puppet.contact_signature(signature)
123 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/user/favorite.py:
--------------------------------------------------------------------------------
1 | """
2 | Python Wechaty - https://github.com/wechaty/python-wechaty
3 |
4 | Authors: Huan LI (李卓桓)
5 | Jingjing WU (吴京京)
6 |
7 | 2020-now @ Copyright Wechaty
8 |
9 | Licensed under the Apache License, Version 2.0 (the 'License');
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | http://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an 'AS IS' BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | """
21 | from __future__ import annotations
22 |
23 | from typing import (
24 | TYPE_CHECKING,
25 | Any,
26 | List,
27 | )
28 | from wechaty_puppet import get_logger
29 |
30 | if TYPE_CHECKING:
31 | from .tag import Tag
32 |
33 | log = get_logger('Favorite')
34 |
35 |
36 | # pylint: disable=R
37 | class Favorite:
38 | """
39 | favorite object which handle the url_link content
40 | """
41 | def __init__(self, favorite_id: str):
42 | self.favorite_id = favorite_id
43 |
44 | def get_id(self) -> str:
45 | """
46 | get favorite_id
47 | :return:
48 | """
49 | log.info('get_id() <%s>', self)
50 | return self.favorite_id
51 |
52 | async def tags(self) -> List[Tag]:
53 | """
54 | get favorite tags
55 | """
56 | # TODO -> favorite tags
57 | return []
58 |
59 | async def find_all(self) -> Any:
60 | """
61 | get all favorite tags
62 | """
63 | # TODO -> find_all
64 | return
65 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/user/image.py:
--------------------------------------------------------------------------------
1 | """
2 | Python Wechaty - https://github.com/wechaty/python-wechaty
3 | 2020-now @ Copyright Wechaty
4 |
5 | GitHub:
6 | TypeScript: https://github.com/wechaty/wechaty/blob/master/src/user/image.ts
7 | Python: https://github.com/wechaty/python-wechaty/blob/master/src/wechaty/user/images.py
8 |
9 | Authors: Huan LI (李卓桓)
10 | Jingjing WU (吴京京)
11 |
12 | Licensed under the Apache License, Version 2.0 (the "License");
13 | you may not use this file except in compliance with the License.
14 | You may obtain a copy of the License at
15 |
16 | http://www.apache.org/licenses/LICENSE-2.0
17 |
18 | Unless required by applicable law or agreed to in writing, software
19 | distributed under the License is distributed on an "AS IS" BASIS,
20 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 | See the License for the specific language governing permissions and
22 | limitations under the License.
23 | """
24 | from __future__ import annotations
25 |
26 | from typing import (
27 | Type,
28 | )
29 |
30 |
31 | from wechaty_puppet import (
32 | FileBox, ImageType, get_logger
33 | )
34 |
35 | from ..accessory import Accessory
36 |
37 | log = get_logger('Image')
38 |
39 |
40 | class Image(Accessory):
41 | """
42 | User Image class
43 | """
44 |
45 | def __str__(self) -> str:
46 | return 'Image<%s>' % self.image_id
47 |
48 | def __init__(
49 | self,
50 | image_id: str,
51 | ) -> None:
52 | """
53 | :param image_id:
54 | """
55 | super().__init__()
56 | log.info('init the message Image object <%s>', image_id)
57 |
58 | self.image_id = image_id
59 |
60 | @classmethod
61 | def create(cls: Type[Image], image_id: str) -> Image:
62 | """
63 | create image instance by image_id
64 | :param cls:
65 | :param image_id:
66 | :return:
67 | """
68 | log.info('@classmethod create(%s)', image_id)
69 | return cls(image_id)
70 |
71 | async def thumbnail(self) -> FileBox:
72 | """
73 | docstring
74 | :return:
75 | """
76 | log.info('thumbnail() for <%s>', self.image_id)
77 | image_file = await self.puppet.message_image(
78 | message_id=self.image_id, image_type=ImageType.IMAGE_TYPE_HD)
79 | return image_file
80 |
81 | async def hd(self) -> FileBox:
82 | """
83 | docstring
84 | :return:
85 | """
86 | log.info('hd() for <%s>', self.image_id)
87 | image_file = await self.puppet.message_image(
88 | message_id=self.image_id, image_type=ImageType.IMAGE_TYPE_HD)
89 | return image_file
90 |
91 | async def artwork(self) -> FileBox:
92 | """
93 | docstring
94 | :return:
95 | """
96 | log.info('artwork() for <%s>', self.image_id)
97 | image_file = await self.puppet.message_image(
98 | message_id=self.image_id, image_type=ImageType.IMAGE_TYPE_ARTWORK)
99 | return image_file
100 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/user/message.pyi:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 | from logging import Logger
3 | from ..accessory import Accessory as Accessory
4 | from .contact import Contact as Contact
5 | from .image import Image as Image
6 | from .mini_program import MiniProgram as MiniProgram
7 | from .room import Room as Room
8 | from .url_link import UrlLink as UrlLink
9 | from _typeshed import Incomplete
10 | from datetime import datetime
11 | from typing import List, Optional, Union, overload
12 | from wechaty.exceptions import WechatyOperationError as WechatyOperationError, WechatyPayloadError as WechatyPayloadError
13 | from wechaty.user.contact_self import ContactSelf as ContactSelf
14 | from wechaty.utils import timestamp_to_date as timestamp_to_date
15 | from wechaty_puppet import FileBox, MessagePayload, MessageType
16 |
17 | log: Logger
18 | SUPPORTED_MESSAGE_FILE_TYPES: List[MessageType]
19 |
20 | class Message(Accessory[MessagePayload]):
21 | Type: MessageType
22 | message_id: str
23 |
24 | def __init__(self, message_id: str) -> None: ...
25 | def message_type(self) -> MessageType: ...
26 |
27 | @overload
28 | async def say(self, msg: str) -> Optional[Message]: ...
29 | @overload
30 | async def say(self, msg: str, mention_ids: List[str] = ...) -> Optional[Message]: ...
31 | @overload
32 | async def say(self, msg: Union[Contact, FileBox, UrlLink, MiniProgram]) -> Optional[Message]: ...
33 |
34 | @classmethod
35 | async def find(cls, talker_id: Optional[str] = ..., message_id: Optional[str] = ..., room_id: Optional[str] = ..., text: Optional[str] = ..., to_id: Optional[str] = ..., message_type: Optional[MessageType] = ...) -> Optional[Message]: ...
36 | @classmethod
37 | async def find_all(cls, talker_id: Optional[str] = ..., message_id: Optional[str] = ..., room_id: Optional[str] = ..., text: Optional[str] = ..., to_id: Optional[str] = ..., message_type: Optional[MessageType] = ...) -> List[Message]: ...
38 | def talker(self) -> Contact: ...
39 | def to(self) -> Optional[Contact]: ...
40 | def room(self) -> Optional[Room]: ...
41 | def chatter(self) -> Union[Room, Contact]: ...
42 | def text(self) -> str: ...
43 | async def to_recalled(self) -> Message: ...
44 | async def recall(self) -> bool: ...
45 | @classmethod
46 | def load(cls, message_id: str) -> Message: ...
47 | def type(self) -> MessageType: ...
48 | def is_self(self) -> bool: ...
49 | async def mention_list(self) -> List[Contact]: ...
50 | async def mention_text(self) -> str: ...
51 | async def mention_self(self) -> bool: ...
52 | payload: Incomplete
53 | async def ready(self) -> None: ...
54 | async def forward(self, to: Union[Room, Contact]) -> None: ...
55 | def date(self) -> datetime: ...
56 | def age(self) -> int: ...
57 | async def to_file_box(self) -> FileBox: ...
58 | def to_image(self) -> Image: ...
59 | async def to_contact(self) -> Contact: ...
60 | async def to_url_link(self) -> UrlLink: ...
61 | async def to_mini_program(self) -> MiniProgram: ...
62 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/user/mini_program.py:
--------------------------------------------------------------------------------
1 | """
2 | Python Wechaty - https://github.com/wechaty/python-wechaty
3 |
4 | Authors: Huan LI (李卓桓)
5 | Jingjing WU (吴京京)
6 |
7 | 2020-now @ Copyright Wechaty
8 |
9 | Licensed under the Apache License, Version 2.0 (the 'License');
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | http://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an 'AS IS' BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | """
21 | from __future__ import annotations
22 |
23 | from typing import TYPE_CHECKING
24 | from dataclasses import asdict
25 |
26 | from wechaty import Accessory
27 | from wechaty_puppet import MiniProgramPayload, get_logger
28 | from wechaty.utils import default_str
29 |
30 | if TYPE_CHECKING:
31 | from wechaty.user import Message
32 |
33 |
34 | log = get_logger('MiniProgram')
35 |
36 |
37 | class MiniProgram(Accessory[MiniProgramPayload]):
38 | """
39 | mini_program object which handle the url_link content
40 | """
41 | def __init__(self, payload: MiniProgramPayload):
42 | """
43 | initialization for mini_program
44 | :param payload:
45 | """
46 | super().__init__()
47 |
48 | log.info('MiniProgram created')
49 | self._payload: MiniProgramPayload = payload
50 |
51 | @classmethod
52 | async def create_from_message(cls, message: Message) -> MiniProgram:
53 | """
54 | static create MiniProgram method
55 | :return:
56 | """
57 | log.info(f'loading the mini-program from message <{message}>')
58 |
59 | mini_program_payload = await cls.get_puppet().message_mini_program(
60 | message_id=message.message_id)
61 |
62 | mini_program = MiniProgram(mini_program_payload)
63 | return mini_program
64 |
65 | @classmethod
66 | def create_from_json(cls, payload_data: dict) -> MiniProgram:
67 | """
68 | create the mini_program from json data
69 | """
70 | log.info(f'loading the mini-program from json data <{payload_data}>')
71 |
72 | payload = MiniProgramPayload(**payload_data)
73 |
74 | mini_program = cls(payload=payload)
75 | return mini_program
76 |
77 | def to_json(self) -> dict:
78 | """
79 | save the mini-program to dict data
80 | """
81 | log.info(f'save the mini-program to json data : <{self.payload}>')
82 | mini_program_data = asdict(self.payload)
83 | return mini_program_data
84 |
85 | @property
86 | def app_id(self) -> str:
87 | """
88 | get mini_program app_id
89 | :return:
90 | """
91 | return default_str(self._payload.appid)
92 |
93 | @property
94 | def title(self) -> str:
95 | """
96 | get mini_program title
97 | :return:
98 | """
99 | return default_str(self._payload.title)
100 |
101 | @property
102 | def icon_url(self) -> str:
103 | """
104 | get mini_program icon url
105 | """
106 | return default_str(self._payload.iconUrl)
107 |
108 | @property
109 | def page_path(self) -> str:
110 | """
111 | get mini_program page_path
112 | :return:
113 | """
114 | return default_str(self._payload.pagePath)
115 |
116 | @property
117 | def user_name(self) -> str:
118 | """
119 | get mini_program user_name
120 | :return:
121 | """
122 | return default_str(self._payload.username)
123 |
124 | @property
125 | def description(self) -> str:
126 | """
127 | get mini_program description
128 | :return:
129 | """
130 | return default_str(self._payload.description)
131 |
132 | @property
133 | def thumb_url(self) -> str:
134 | """
135 | get mini_program thumb_url
136 | :return:
137 | """
138 | return default_str(self._payload.thumbUrl)
139 |
140 | @property
141 | def thumb_key(self) -> str:
142 | """
143 | get mini_program thumb_key
144 | :return:
145 | """
146 | return default_str(self._payload.thumbKey)
147 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/user/room.pyi:
--------------------------------------------------------------------------------
1 | from __future__ import annotations
2 | from ..accessory import Accessory as Accessory
3 | from ..config import AT_SEPARATOR as AT_SEPARATOR, PARALLEL_TASK_NUM as PARALLEL_TASK_NUM
4 | from .contact import Contact as Contact
5 | from .message import Message as Message
6 | from .mini_program import MiniProgram as MiniProgram
7 | from .url_link import UrlLink as UrlLink
8 | from _typeshed import Incomplete
9 | from typing import Any, Callable, List, Optional, Union, overload
10 | from wechaty.exceptions import WechatyOperationError as WechatyOperationError, WechatyPayloadError as WechatyPayloadError
11 | from wechaty.user.contact_self import ContactSelf as ContactSelf
12 | from wechaty.utils.async_helper import gather_with_concurrency as gather_with_concurrency
13 | from wechaty_puppet import FileBox, RoomMemberQueryFilter, RoomPayload, RoomQueryFilter
14 |
15 | log: Incomplete
16 |
17 | class Room(Accessory[RoomPayload]):
18 | room_id: Incomplete
19 | def __init__(self, room_id: str) -> None: ...
20 | def on(self, event_name: str, func: Callable[..., Any]) -> None: ...
21 | def emit(self, event_name: str, *args: Any, **kwargs: Any) -> None: ...
22 | @classmethod
23 | async def create(cls, contacts: List[Contact], topic: str) -> Room: ...
24 | @classmethod
25 | async def find_all(cls, query: Optional[Union[str, RoomQueryFilter, Callable[[Contact], bool]]] = ...) -> List[Room]: ...
26 | @classmethod
27 | async def find(cls, query: Union[str, RoomQueryFilter, Callable[[Room], bool]] = ...) -> Optional[Room]: ...
28 | @classmethod
29 | def load(cls, room_id: str) -> Room: ...
30 | payload: Incomplete
31 | async def ready(self, force_sync: bool = ..., load_members: bool = ...) -> None: ...
32 | @overload
33 | async def say(self, msg: str) -> Optional[Message]: ...
34 | @overload
35 | async def say(self, msg: str, mention_ids: List[str] = ...) -> Optional[Message]: ...
36 | @overload
37 | async def say(self, msg: Union[Contact, FileBox, UrlLink, MiniProgram]) -> Optional[Message]: ...
38 |
39 | async def add(self, contact: Contact) -> None: ...
40 | async def delete(self, contact: Contact) -> None: ...
41 | async def quit(self) -> None: ...
42 | async def topic(self, new_topic: str = ...) -> Optional[str]: ...
43 | async def announce(self, announce_text: str = ...) -> Optional[str]: ...
44 | async def qr_code(self) -> str: ...
45 | async def alias(self, member: Contact) -> Optional[str]: ...
46 | async def has(self, contact: Contact) -> bool: ...
47 | async def member_list(self, query: Union[str, RoomMemberQueryFilter] = ...) -> List[Contact]: ...
48 | async def member(self, query: Union[str, RoomMemberQueryFilter] = ...) -> Optional[Contact]: ...
49 | async def owner(self) -> Optional[Contact]: ...
50 | async def avatar(self) -> FileBox: ...
51 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/user/tag.py:
--------------------------------------------------------------------------------
1 | """
2 | Tag for Contact Message
3 | """
4 | from __future__ import annotations
5 |
6 | from typing import (
7 | Dict,
8 | # Optional,
9 | Union,
10 | TYPE_CHECKING
11 | )
12 |
13 | from collections import defaultdict
14 |
15 | from wechaty.exceptions import WechatyOperationError
16 | from wechaty_puppet import get_logger
17 | from ..accessory import (
18 | Accessory,
19 | )
20 |
21 | if TYPE_CHECKING:
22 | from .contact import Contact
23 | from .favorite import Favorite
24 |
25 | log = get_logger('Tag')
26 |
27 |
28 | class Tag(Accessory):
29 | """
30 | tag object which handle the url_link content
31 | """
32 | _pool: Dict[str, 'Tag'] = defaultdict()
33 |
34 | tag_id: str
35 |
36 | def __init__(self, tag_id: str) -> None:
37 | """
38 | initialization for tag base class
39 | :param tag_id:
40 | """
41 | super().__init__()
42 | log.info('create tag %s', tag_id)
43 |
44 | self.tag_id = tag_id
45 |
46 | @classmethod
47 | def load(cls, tag_id: str) -> Tag:
48 | """
49 | load tag instance
50 | """
51 | if tag_id in cls._pool:
52 | return cls._pool[tag_id]
53 |
54 | new_tag = cls(tag_id)
55 | cls._pool[tag_id] = new_tag
56 | return new_tag
57 |
58 | @classmethod
59 | def get(cls, tag_id: str) -> Tag:
60 | """
61 | get tag objecr
62 | """
63 | log.info('load tag object %s', tag_id)
64 | return cls.load(tag_id)
65 |
66 | async def delete(self, target: Union[Contact, Favorite]) -> None:
67 | """
68 | remove tag from contact or favorite
69 | :param target:
70 | :return:
71 | """
72 | log.info('delete tag %s', self.tag_id)
73 |
74 | if target is Contact:
75 | await self.puppet.tag_contact_delete(tag_id=self.tag_id)
76 | elif target is Favorite:
77 | # TODO -> tag_favorite_delete not implement
78 | pass
79 | # await self.puppet.tag_contact_delete()
80 | else:
81 | raise WechatyOperationError('target param is required to be Contact or Favorite object')
82 |
83 | async def add(self, to: Union[Contact, Favorite]) -> None:
84 | """
85 | add tag to contact or favorite
86 | :param to:
87 | :return:
88 | """
89 | log.info('add tag to %s', str(to))
90 | if isinstance(to, Contact):
91 | await self.puppet.tag_contact_add(
92 | tag_id=self.tag_id, contact_id=to.contact_id
93 | )
94 | elif isinstance(to, Favorite):
95 | # TODO -> tag_favorite_add not implement
96 | pass
97 | # self.puppet.tag_favorite_add(self.tag_id, to)
98 |
99 | def remove(self, source: Union[Contact, Favorite]) -> None:
100 | """
101 | Remove this tag from Contact/Favorite
102 |
103 | tips : This function is depending on the Puppet Implementation,
104 | see [puppet-compatible-table](https://github.com/wechaty/
105 | wechaty/wiki/Puppet#3-puppet-compatible-table)
106 | :param source:
107 | :return:
108 | """
109 | log.info('remove tag for %s with %s',
110 | self.tag_id,
111 | str(source))
112 | try:
113 | if isinstance(source, Contact):
114 | self.puppet.tag_contact_remove(
115 | tag_id=self.tag_id, contact_id=source.contact_id)
116 | elif isinstance(source, Favorite):
117 | # TODO -> tag_favorite_remove not implement
118 | pass
119 | except Exception as e:
120 | log.info('remove exception %s', str(e.args))
121 | raise WechatyOperationError('remove error')
122 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/user/url_link.py:
--------------------------------------------------------------------------------
1 | """
2 | UrlLink for Contact Message
3 | """
4 | from __future__ import annotations
5 |
6 | from typing import (
7 | Optional,
8 | Type
9 | )
10 | from wechaty_puppet import UrlLinkPayload, get_logger
11 |
12 | from wechaty.utils.link import get_url_metadata
13 |
14 |
15 |
16 | log = get_logger('UrlLink')
17 |
18 |
19 | class UrlLink:
20 | """
21 | url_link object which handle the url_link content
22 | """
23 |
24 | def __init__(
25 | self,
26 | payload: UrlLinkPayload,
27 | ):
28 | """
29 | initialization
30 | :param payload:
31 | """
32 | self.payload: UrlLinkPayload = payload
33 |
34 | @classmethod
35 | def create(
36 | cls: Type[UrlLink],
37 | url: str,
38 | title: Optional[str] = None,
39 | thumbnail_url: Optional[str] = None,
40 | description: Optional[str] = None
41 | ) -> UrlLink:
42 | """
43 | create urllink from url string
44 | """
45 | log.info('create url_link for %s', url)
46 |
47 | metadata = get_url_metadata(url)
48 |
49 | payload = UrlLinkPayload(url=url)
50 |
51 | payload.title = title or metadata.get('title', None)
52 | payload.thumbnailUrl = thumbnail_url or metadata.get('image', None)
53 | payload.description = description or metadata.get('description', None)
54 | return UrlLink(payload)
55 |
56 | def __str__(self) -> str:
57 | """
58 | UrlLink string format output
59 | :return:
60 | """
61 | return 'UrlLink<%s>' % self.payload.url
62 |
63 | @property
64 | def title(self) -> str:
65 | """
66 | get UrlLink title
67 | :return:
68 | """
69 | return self.payload.title or ''
70 |
71 | @property
72 | def thumbnailUrl(self) -> str:
73 | """
74 | get thumbnail url
75 | :return:
76 | """
77 | return self.payload.thumbnailUrl or ''
78 |
79 | @property
80 | def description(self) -> str:
81 | """
82 | get description
83 | :return:
84 | """
85 | return self.payload.description or ''
86 |
87 | @property
88 | def url(self) -> str:
89 | """
90 | get url
91 | :return:
92 | """
93 | return self.payload.url or ''
94 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/utils/__init__.py:
--------------------------------------------------------------------------------
1 | """doc"""
2 | from .qr_code import qr_terminal
3 | from .type_check import default_str
4 | from .date_util import timestamp_to_date
5 | # from .type_check import type_check
6 |
7 | __all__ = [
8 | 'qr_terminal',
9 | 'default_str',
10 | 'timestamp_to_date'
11 | # 'type_check'
12 | ]
13 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/utils/async_helper.py:
--------------------------------------------------------------------------------
1 | """
2 | async helpers
3 | """
4 | from __future__ import annotations
5 | import asyncio
6 | from asyncio import Task
7 | from typing import (
8 | List,
9 | Any,
10 | Optional,
11 | Set,
12 | TYPE_CHECKING
13 | )
14 | import functools
15 |
16 | if TYPE_CHECKING:
17 | from wechaty import Message, WechatyPlugin
18 |
19 |
20 | async def gather_with_concurrency(n_task: int, tasks: List[Task]) -> Any:
21 | """
22 | gather tasks with the specific number concurrency
23 | Args:
24 | n_task: the number of tasks
25 | tasks: task objects
26 | """
27 | semaphore = asyncio.Semaphore(n_task)
28 |
29 | async def sem_task(task: Task) -> Any:
30 | async with semaphore:
31 | return await task
32 | return await asyncio.gather(*(sem_task(task) for task in tasks))
33 |
34 |
35 | class SingleIdContainer:
36 | """Store the Message Id Container"""
37 | _instance: Optional[SingleIdContainer] = None
38 |
39 | def __init__(self) -> None:
40 | self.ids: Set[str] = set()
41 | self.max_size: int = 100000
42 |
43 | def exist(self, message_id: str) -> bool:
44 | """exist if the message has been emitted
45 |
46 | Args:
47 | message_id (str): the identifier of message
48 |
49 | Returns:
50 | bool: if the message is the first message
51 | """
52 | if message_id in self.ids:
53 | return True
54 | self.ids.add(message_id)
55 | return False
56 |
57 | @classmethod
58 | def instance(cls) -> SingleIdContainer:
59 | """singleton pattern for MessageIdContainer"""
60 | if cls._instance is None or len(cls._instance.ids) > cls._instance.max_size:
61 | cls._instance = SingleIdContainer()
62 |
63 | return cls._instance
64 |
65 |
66 | def single_message(on_message_func): # type: ignore
67 | """single message decorator"""
68 | @functools.wraps(on_message_func)
69 | async def wrapper(plugin: WechatyPlugin, message: Message) -> None:
70 | if not SingleIdContainer.instance().exist(message.message_id):
71 | await on_message_func(plugin, message)
72 | return wrapper
73 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/utils/date_util.py:
--------------------------------------------------------------------------------
1 | """
2 | Python Wechaty - https://github.com/wechaty/python-wechaty
3 |
4 | Authors: Huan LI (李卓桓)
5 | Jingjing WU (吴京京)
6 |
7 | 2020-now @ Copyright Wechaty
8 |
9 | Licensed under the Apache License, Version 2.0 (the 'License');
10 | you may not use this file except in compliance with the License.
11 | You may obtain a copy of the License at
12 |
13 | http://www.apache.org/licenses/LICENSE-2.0
14 |
15 | Unless required by applicable law or agreed to in writing, software
16 | distributed under the License is distributed on an 'AS IS' BASIS,
17 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 | See the License for the specific language governing permissions and
19 | limitations under the License.
20 | """
21 | from __future__ import annotations
22 | from datetime import datetime
23 |
24 |
25 | def timestamp_to_date(timestamp: float) -> datetime:
26 | """convert different timestamp precision to python format
27 |
28 | Python2.7: https://docs.python.org/2.7/library/datetime.html#datetime.datetime
29 | Python3+ :https://docs.python.org/3.7/library/datetime.html#datetime.datetime
30 | for datetime.fromtimestamp. It’s common for this to be restricted to years from 1970 through 2038.
31 | 2145888000 is 2038-01-01 00:00:00 UTC for second
32 | 2145888000 is 1970-01-26 04:04:48 UTC for millisecond
33 |
34 | Args:
35 | timestamp (float): from different source, so, has different
36 | timestamp precision
37 | """
38 | if timestamp > 2145888000:
39 | timestamp = timestamp / 1000
40 | return datetime.fromtimestamp(timestamp)
41 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/utils/link.py:
--------------------------------------------------------------------------------
1 | """link helper function for fetching the friendly meta data from url link"""
2 | from __future__ import annotations
3 | from typing import Any, Dict
4 | import requests
5 | from opengraph_py3 import OpenGraph # type: ignore
6 |
7 |
8 | def get_url_metadata(url: str) -> Dict[str, Any]:
9 | """get open graph meta data open open graph protocol
10 |
11 | The Open Graph Protocol: https://ogp.me/
12 |
13 | Args:
14 | url (str): the url of link
15 |
16 | Returns:
17 | Dict[str, Any]: the meta data
18 | """
19 | return OpenGraph(url=url)
20 |
21 |
22 | def fetch_github_user_avatar_url(name: str) -> str:
23 | """fetch_github_user_avatar_url
24 |
25 | refer to: https://docs.github.com/en/rest/users/users#get-a-user
26 |
27 | Args:
28 | name (str): the name of github user
29 |
30 | Returns:
31 | str: the avatar url of github user
32 | """
33 | source_avatar_url = f'https://api.github.com/users/{name}'
34 |
35 | header = {
36 | "accept": "application/vnd.github.v3+jso"
37 | }
38 | response = requests.get(source_avatar_url, headers=header)
39 | data = response.json()
40 | return data.get('avatar_url', None)
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/utils/qr_code.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | qr_code helper utils
4 | """
5 | from typing import Any
6 | import qrcode
7 |
8 |
9 | def qr_terminal(data: str, version: Any = None) -> None:
10 | """print the qrcode to the terminal using the python-qrcode tools
11 |
12 | https://github.com/lincolnloop/python-qrcode
13 |
14 | Args:
15 | data (str): the data of the qrcode
16 | version (Any, optional): the qrcode version. Defaults to None.
17 | """
18 | qr = qrcode.QRCode(version, border=2)
19 | qr.add_data(data)
20 | if version:
21 | qr.make()
22 | else:
23 | qr.make(fit=True)
24 | qr.print_ascii(invert=True)
25 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/utils/qrcode_terminal.py:
--------------------------------------------------------------------------------
1 | from typing import Any
2 | import qrcode
3 | import platform
4 |
5 |
6 | def qr_terminal_str(data: Any, version: Any = None) -> str:
7 | """
8 |
9 | :param data: qrcode data
10 | :param version:1-40 or None
11 | :return:
12 | """
13 | if platform.system() == "Windows":
14 | white_block = '▇'
15 | black_block = ' '
16 | new_line = '\n'
17 | else:
18 | white_block = '\033[0;37;47m '
19 | black_block = '\033[0;37;40m '
20 | new_line = '\033[0m\n'
21 |
22 | qr = qrcode.QRCode(version)
23 | qr.add_data(data)
24 | if version:
25 | qr.make()
26 | else:
27 | qr.make(fit=True)
28 | output = white_block * (qr.modules_count + 2) + new_line
29 | for mn in qr.modules:
30 | output += white_block
31 | for m in mn:
32 | if m:
33 | output += black_block
34 | else:
35 | output += white_block
36 | output += white_block + new_line
37 | output += white_block * (qr.modules_count + 2) + new_line
38 | return output
39 |
40 |
41 | def draw(data: Any, version: Any = None) -> None:
42 | """doc"""
43 | output = qr_terminal_str(data, version)
44 | print(output)
45 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/utils/type_check.py:
--------------------------------------------------------------------------------
1 | """add type check utils"""
2 | from typing import Union, Optional
3 |
4 |
5 | def type_check(obj: object, type_name: str) -> bool:
6 | """
7 | circulation dependency problems can be resolved by TYPE_CHECKING,
8 | but this can not resolve NO type linting problems. eg:
9 | if isinstance(msg, Contact):
10 | pass
11 | in this problem, program don't import Contact at running time. So, it will
12 | throw a Exception, which will not be threw
13 | :param obj:
14 | :param type_name:
15 | :return:
16 | """
17 | if hasattr(obj, '__class__') and hasattr(obj.__class__, '__name__'):
18 | return obj.__class__.__name__ == type_name
19 | return False
20 |
21 |
22 | def default_str(obj: Union[str, Optional[str]]) -> str:
23 | if obj:
24 | return obj
25 | return ''
26 |
--------------------------------------------------------------------------------
/python-wechaty/src/wechaty/version.py:
--------------------------------------------------------------------------------
1 | """
2 | Do not edit this file.
3 | This file will be auto-generated before deploy.
4 | """
5 | VERSION = '0.0.0'
6 |
--------------------------------------------------------------------------------
/python-wechaty/tests/config_test.py:
--------------------------------------------------------------------------------
1 | """
2 | config unit test
3 | """
4 | from typing import (
5 | Any,
6 | # Dict,
7 | Iterable,
8 | )
9 |
10 | import pytest
11 |
12 | from wechaty_puppet import get_logger
13 |
14 | # pylint: disable=C0103
15 | log = get_logger('ConfigTest')
16 |
17 | # pylint: disable=redefined-outer-name
18 |
19 |
20 | # https://stackoverflow.com/a/57015304/1123955
21 | @pytest.fixture(name='data', scope='module')
22 | def fixture_data() -> Iterable[str]:
23 | """ doc """
24 | yield 'test'
25 |
26 |
27 | def test_config(
28 | data: Any,
29 | ) -> None:
30 | """
31 | Unit Test for config function
32 | """
33 | print(data)
34 |
35 | assert data == 'test', 'data should equals test'
36 |
37 |
38 | def test_get_logger() -> None:
39 | """test"""
40 | assert get_logger, 'log should exist'
41 |
--------------------------------------------------------------------------------
/python-wechaty/tests/room_test.py:
--------------------------------------------------------------------------------
1 | from typing import Union
2 | import pytest
3 | from wechaty.wechaty import Wechaty # noqa
4 |
5 |
6 | @pytest.mark.asyncio
7 | async def test_room_owner(test_bot: Wechaty) -> None:
8 | owner = await test_bot.Room("fake_room").owner()
9 | await owner.ready()
10 | assert owner.contact_id == "wechaty_user"
11 |
12 |
13 | @pytest.mark.asyncio
14 | async def test_room_topic(test_bot: Wechaty) -> None:
15 | topic = await test_bot.Room("fake_room").topic()
16 | assert topic == "fake_room"
17 |
18 |
19 | @pytest.mark.parametrize(
20 | ("room_name", "res"), [("test", "test_room"), ("fake", "fake_room"), ("wechaty", None)]
21 | )
22 | @pytest.mark.asyncio
23 | async def test_room_find(test_bot: Wechaty, room_name: str, res: Union[str, None]) -> None:
24 | room = await test_bot.Room.find(room_name)
25 | name = room.room_id if room else None
26 | assert name == res
27 |
28 |
29 | @pytest.mark.parametrize(
30 | ("room_name", "res"), [("test", 1), ("fake", 1), ("room", 2), ("wechaty", 0)]
31 | )
32 | @pytest.mark.asyncio
33 | async def test_room_findall(test_bot: Wechaty, room_name: str, res: int) -> None:
34 | room = await test_bot.Room.find_all(room_name)
35 | assert len(room) == res
36 |
--------------------------------------------------------------------------------
/python-wechaty/tests/smoke_testing_test.py:
--------------------------------------------------------------------------------
1 | """
2 | Unit Test
3 | """
4 | # pylint: disable=W0621
5 |
6 | # from typing import (
7 | # # Any,
8 | # Iterable,
9 | # )
10 |
11 | import pytest
12 |
13 | # from agent import Agent
14 |
15 |
16 | def test_smoke_testing() -> None:
17 | """ wechaty """
18 | assert pytest, 'should True'
19 |
--------------------------------------------------------------------------------
/python-wechaty/tests/timestamp_test.py:
--------------------------------------------------------------------------------
1 | """
2 | Unit test
3 | """
4 | from wechaty.utils import timestamp_to_date
5 |
6 |
7 | def test_timestamp_with_millisecond_precision() -> None:
8 | timestamp = timestamp_to_date(1600849574736)
9 | assert timestamp is not None
10 |
11 |
12 | def test_timestamp_with_microsecond_precision() -> None:
13 | timestamp = timestamp_to_date(1600849792.367416)
14 | assert timestamp is not None
15 |
--------------------------------------------------------------------------------
/python-wechaty/tests/url_link_test.py:
--------------------------------------------------------------------------------
1 | """unit test for urllink"""
2 | from __future__ import annotations
3 |
4 | from wechaty.user.url_link import UrlLink
5 |
6 |
7 | def test_create():
8 | """unit test for creating"""
9 | UrlLink.create(
10 | url='https://github.com/wechaty/python-wechaty/issues/339',
11 | title='title',
12 | thumbnail_url='thu',
13 | description='simple desc'
14 | )
15 |
--------------------------------------------------------------------------------
/python-wechaty/tests/user_message_test.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from wechaty.wechaty import Wechaty
3 |
4 |
5 | @pytest.mark.asyncio
6 | async def test_mention_text_without_mentions(test_bot: Wechaty) -> None:
7 | """Test extracting mention text from a message without mentions"""
8 | msg = await test_bot.Message.find(message_id="no_mention")
9 | await msg.ready()
10 | text = await msg.mention_text()
11 | assert text == 'foo bar asd'
12 |
13 |
14 | @pytest.mark.asyncio
15 | async def test_mention_text_without_mentions_in_room(test_bot: Wechaty) -> None:
16 | """Test extracting mention text from a message without mentions"""
17 | msg = await test_bot.Message.find(message_id="room_no_mention")
18 | await msg.ready()
19 | text = await msg.mention_text()
20 | assert text == 'beep'
21 |
22 |
23 | @pytest.mark.asyncio
24 | async def test_mention_text_with_mentions_in_room(test_bot: Wechaty) -> None:
25 | """Test extracting mention text from a message without mentions"""
26 | msg = await test_bot.Message.find(message_id="room_with_mentions")
27 | await msg.ready()
28 | text = await msg.mention_text()
29 | assert text == 'test message asd'
30 |
31 |
32 | @pytest.mark.asyncio
33 | async def test_mention_text_with_mentions_and_alias_in_room(test_bot: Wechaty) -> None:
34 | """Test extracting mention text from a message without mentions"""
35 | msg = await test_bot.Message.find(message_id="room_with_mentions_and_alias")
36 | await msg.ready()
37 | text = await msg.mention_text()
38 | assert text == '123123 kkasd'
39 |
40 |
41 | @pytest.mark.asyncio
42 | async def test_mention_text_with_mentions_and_mismatched_alias(test_bot: Wechaty) -> None:
43 | """Test extracting mention text from a message without mentions"""
44 | msg = await test_bot.Message.find(message_id="room_with_mentions_and_alias_mismatched")
45 | await msg.ready()
46 | text = await msg.mention_text()
47 | assert text == '123123@Fake User beep'
48 |
49 |
50 | @pytest.mark.asyncio
51 | async def test_mention_text_with_mentions_but_not_mention_data(test_bot: Wechaty) -> None:
52 | """Test extracting mention text from a message without mentions"""
53 | msg = await test_bot.Message.find(message_id="room_with_text_mentions")
54 | await msg.ready()
55 | text = await msg.mention_text()
56 | assert text == '@Wechaty User @Test User @Fake Alias beep!!'
57 |
--------------------------------------------------------------------------------
/python-wechaty/tests/utils_test.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import pytest
3 | from wechaty.utils.async_helper import gather_with_concurrency
4 | from wechaty.utils.link import fetch_github_user_avatar_url, get_url_metadata
5 | from wechaty.utils.async_helper import SingleIdContainer
6 |
7 |
8 | async def number_task(num: int):
9 | """
10 | just return the original number
11 | """
12 | return num
13 |
14 |
15 | @pytest.mark.asyncio
16 | async def test_gather_tasks_with_n_concurrency():
17 | tasks = [asyncio.create_task(number_task(i)) for i in range(1000)]
18 | sum_value = (0 + 999) * 1000 / 2
19 | result = await gather_with_concurrency(10, tasks)
20 | assert sum_value == sum(result), 'the final sum value is not correct'
21 |
22 |
23 | def test_fetch_metadata():
24 | metadata = get_url_metadata('http://github.com/')
25 | assert 'title' in metadata
26 | assert 'image' in metadata
27 |
28 |
29 | def test_fetch_github_user_avatar():
30 | avatar = fetch_github_user_avatar_url('wj-Mcat')
31 | assert avatar is not None
32 | assert 'avatars.githubusercontent.com' in avatar
33 |
34 |
35 | def test_single_id_container():
36 | assert not SingleIdContainer.instance().exist('-1')
37 | assert SingleIdContainer.instance().exist('-1')
38 |
39 | for index in range(SingleIdContainer.instance().max_size):
40 | assert not SingleIdContainer.instance().exist(index)
41 |
42 | assert len(SingleIdContainer.instance().ids) == 0
--------------------------------------------------------------------------------
/python-wechaty/tests/version_test.py:
--------------------------------------------------------------------------------
1 | """
2 | version unit test
3 | """
4 | # import pytest # type: ignore
5 |
6 | from wechaty.version import VERSION
7 |
8 |
9 | def test_version() -> None:
10 | """
11 | Unit Test for version file
12 | """
13 |
14 | assert VERSION == '0.0.0', 'version should be 0.0.0'
15 |
--------------------------------------------------------------------------------
/python-wechaty/tests/wechaty_test.py:
--------------------------------------------------------------------------------
1 | """
2 | Unit test
3 | """
4 | import pytest
5 | from wechaty_puppet import WechatyPuppetConfigurationError
6 | from wechaty import Wechaty, WechatyOptions
7 |
8 | def test_constructor():
9 | with pytest.raises(WechatyPuppetConfigurationError):
10 | bot = Wechaty()
11 |
12 | options = WechatyOptions(token='fake-token', endpoint='127.0.0.1:8080')
13 | bot = Wechaty(options=options)
14 |
15 | assert bot.puppet.options.token == 'fake-token'
16 | assert bot.puppet.options.end_point == '127.0.0.1:8080'
17 |
--------------------------------------------------------------------------------
/python-wechaty/wip/wechaty/__init__.py:
--------------------------------------------------------------------------------
1 | """doc"""
2 | from __future__ import annotations
3 |
4 | from typing import (
5 | Optional,
6 | )
7 |
8 |
9 | class Wechaty:
10 | """Working In Progress"""
11 |
12 | def __init__(self) -> None:
13 | """WIP Warning"""
14 | print('''
15 |
16 | Dear Python Wechaty user,
17 |
18 | Thank you very much for using Python Wechaty!
19 |
20 | Wechaty is a RPA SDK for Wechat Individual Account that can help you create a chatbot in 6 lines of Python.
21 | Our GitHub is at https://github.com/wechaty/python-wechaty
22 |
23 | Today, we are under the process of translating the TypeScript Wechaty to Python Wechaty, please see issue #11 "From TypeScript to Python in Wechaty Way - Internal Modules" at https://github.com/wechaty/python-wechaty/issues/11
24 |
25 | To stay tuned, watch our repository now!
26 |
27 | Please also feel free to leave comments in our issues if you want to contribute, testers, coders, and doc writers are all welcome!
28 |
29 | Huan
30 | Author of Wechaty
31 | Mar 15, 2020
32 |
33 | ''')
34 |
35 | def version(self) -> str:
36 | """version"""
37 | type(self)
38 | return '0.0.0'
39 |
40 | def ding(
41 | self: Wechaty,
42 | data: Optional[str],
43 | ) -> None:
44 | """ding"""
45 | type(self)
46 | type(data)
47 |
48 |
49 | __all__ = [
50 | 'Wechaty',
51 | ]
52 |
--------------------------------------------------------------------------------
/rasa_chatbot_cn/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__
2 | *.log
3 | failed_stories.md
4 | story_confmat.md
5 | test_env
6 |
--------------------------------------------------------------------------------
/rasa_chatbot_cn/Makefile:
--------------------------------------------------------------------------------
1 | train:
2 | rasa train --domain domain.yml --data data --config config.yml --out models
3 |
4 | train-nlu:
5 | rasa train nlu --nlu split_data/nlu --config configs.yml --out models/nlu
6 |
7 | run-actions:
8 | rasa run actions
9 |
10 | shell:
11 | make run-actions &
12 | rasa shell -m models --endpoints configs/endpoints.yml
13 |
14 | run:
15 | make run-actions &
16 | rasa run --enable-api -m models --endpoints configs/endpoints.yml -p 5005
17 |
18 | run-x:
19 | make run-actions &
20 | rasa x --no-prompt -c configs.yml --cors "*" --endpoints configs/endpoints.yml --enable-api
--------------------------------------------------------------------------------
/rasa_chatbot_cn/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/rasa_chatbot_cn/__init__.py
--------------------------------------------------------------------------------
/rasa_chatbot_cn/actions/actions.py:
--------------------------------------------------------------------------------
1 | from __future__ import absolute_import
2 | from __future__ import division
3 | from __future__ import print_function
4 | from __future__ import unicode_literals
5 |
6 | from rasa_core_sdk import Action
7 | from rasa_core_sdk.events import SlotSet
8 |
9 |
10 | support_search = ["话费", "流量"]
11 |
12 |
13 | def extract_item(item):
14 | if item is None:
15 | return None
16 | for name in support_search:
17 | if name in item:
18 | return name
19 | return None
20 |
21 |
22 | class ActionSearchConsume(Action):
23 | def name(self):
24 | return 'action_search_consume'
25 |
26 | def run(self, dispatcher, tracker, domain):
27 | item = tracker.get_slot("item")
28 | item = extract_item(item)
29 | if item is None:
30 | dispatcher.utter_message("您好,我现在只会查话费和流量")
31 | dispatcher.utter_message("你可以这样问我:“帮我查话费”")
32 | return []
33 |
34 | time = tracker.get_slot("time")
35 | if time is None:
36 | dispatcher.utter_message("您想查询哪个月的消费?")
37 | return []
38 | # query database here using item and time as key. but you may normalize time format first.
39 | dispatcher.utter_message("好,请稍等")
40 | if item == "流量":
41 | dispatcher.utter_message(
42 | "您好,您{}共使用{}二百八十兆,剩余三十兆。".format(time, item))
43 | else:
44 | dispatcher.utter_message("您好,您{}共消费二十八元。".format(time))
45 | return []
46 |
47 |
--------------------------------------------------------------------------------
/rasa_chatbot_cn/config.yml:
--------------------------------------------------------------------------------
1 | language: zh
2 | pipeline:
3 | - name: HFTransformersNLP
4 | model_weights: "bert-base-chinese"
5 | model_name: "bert"
6 |
7 | - name: LanguageModelTokenizer
8 | - name: LanguageModelFeaturizer
9 |
10 | - name: CountVectorsFeaturizer
11 | - name: CountVectorsFeaturizer
12 | analyzer: char_wb
13 | min_ngram: 1
14 | max_ngram: 4
15 |
16 | - name: DIETClassifier
17 | epochs: 30
18 | learning_rate: 0.005
19 | num_transformer_layers: 0
20 | embedding_dimension: 30
21 | weight_sparcity: 0.8
22 | hidden_layer_sizes:
23 | text: [256, 128]
24 |
25 | policies:
26 | - name: AugmentedMemoizationPolicy
27 |
28 | - name: TEDPolicy
29 | epochs: 100
30 | featurizer:
31 | - name: MaxHistoryTrackerFeaturizer
32 | max_history: 5
33 | state_featurizer:
34 | - name: BinarySingleStateFeaturizer
35 |
36 |
37 | - name: FallbackPolicy
38 | fallback_action_name: 'action_default_fallback'
39 | nlu_threshold: 0.5
40 | core_threshold: 0.3
41 | ambiguity_threshold: 0.1
42 |
43 | - name: MappingPolicy
44 |
--------------------------------------------------------------------------------
/rasa_chatbot_cn/configs/endpoints.yml:
--------------------------------------------------------------------------------
1 | action_endpoint:
2 | url: http://localhost:5055/webhook
3 |
4 | # tracker_store:
5 | # store_type: mongod
6 | # url: mongodb://mongo:27017
7 | # db: chatbot
--------------------------------------------------------------------------------
/rasa_chatbot_cn/data/core/stories.md:
--------------------------------------------------------------------------------
1 | ## Generated Story No1
2 | * greet
3 | - utter_greet
4 | * deny
5 | - utter_goodbye
6 |
7 | ## Generated Story No2
8 | * greet
9 | - utter_greet
10 | * goodbye
11 | - utter_goodbye
12 |
13 | ## Generated Story No3
14 | * greet
15 | - utter_greet
16 | * thanks
17 | - utter_thanks
18 |
19 | ## Generated Story No4
20 | * unknown_intent
21 | - action_default_fallback
22 |
23 | ## Generated Story No5
24 | * greet
25 | - utter_greet
26 | * request_search{"item": "\u8bdd\u8d39"}
27 | - slot{"item": "\u8bdd\u8d39"}
28 | - utter_ask_time
29 | * inform{"time": "\u4e09\u6708"}
30 | - slot{"time": "\u4e09\u6708"}
31 | - utter_confirm
32 | * confirm
33 | - action_search_consume
34 | - utter_ask_morehelp
35 | * deny
36 | - utter_goodbye
37 |
38 |
39 | ## Generated Story No6
40 | * greet
41 | - utter_greet
42 | * request_search{"item": "\u6d41\u91cf", "time": "\u4e09\u6708"}
43 | - slot{"item": "\u6d41\u91cf"}
44 | - slot{"time": "\u4e09\u6708"}
45 | - utter_confirm
46 | * confirm
47 | - action_search_consume
48 | - utter_ask_morehelp
49 | * deny
50 | - utter_goodbye
51 |
52 |
53 | ## Generated Story No7
54 | * greet
55 | - utter_greet
56 | * request_management{"package": "\u5957\u9910"}
57 | - slot{"package": "\u5957\u9910"}
58 | - utter_ask_package
59 | * inform{"package": "\u5957\u9910\u4e00"}
60 | - slot{"package": "\u5957\u9910\u4e00"}
61 | - utter_confirm
62 | * confirm
63 | - utter_ack_management
64 | - utter_ask_morehelp
65 | * deny
66 | - utter_goodbye
67 | * thanks
68 | - utter_thanks
69 |
70 | ## Generated Story No8
71 | * greet
72 | - utter_greet
73 | * request_management{"package": "\u5957\u9910"}
74 | - slot{"package": "\u5957\u9910"}
75 | - utter_ask_package
76 | * inform{"package": "\u5957\u9910\u4e00"}
77 | - slot{"package": "\u5957\u9910\u4e00"}
78 | - utter_confirm
79 | * confirm
80 | - utter_ack_management
81 | - utter_ask_morehelp
82 | * request_search{"item": "\u6d41\u91cf", "time": "\u4e09\u6708"}
83 | - slot{"item": "\u6d41\u91cf"}
84 | - slot{"time": "\u4e09\u6708"}
85 | - utter_confirm
86 | * confirm
87 | - action_search_consume
88 | - utter_ask_morehelp
89 | * deny
90 | - utter_goodbye
91 |
92 | ## Generated Story No9
93 | * greet
94 | - utter_greet
95 | * request_management{"package": "\u5957\u9910"}
96 | - slot{"package": "\u5957\u9910"}
97 | - utter_ask_package
98 | * request_search{"item": "\u6d41\u91cf", "time": "\u4e09\u6708"}
99 | - slot{"item": "\u6d41\u91cf"}
100 | - slot{"time": "\u4e09\u6708"}
101 | - utter_confirm
102 | * confirm
103 | - action_search_consume
104 | - utter_ask_morehelp
105 | * deny
106 | - utter_goodbye
107 |
108 | ## Generated Story No10
109 | * greet
110 | - utter_greet
111 | * request_management{"package": "\u5957\u9910"}
112 | - slot{"package": "\u5957\u9910"}
113 | - utter_ask_package
114 | * request_search{"item": "\u6d41\u91cf"}
115 | - slot{"item": "\u6d41\u91cf"}
116 | - utter_ask_time
117 | * inform{"time": "\u4e09\u6708"}
118 | - slot{"time": "\u4e09\u6708"}
119 | - utter_confirm
120 | * confirm
121 | - action_search_consume
122 | - utter_ask_morehelp
123 | * deny
124 | - utter_goodbye
--------------------------------------------------------------------------------
/rasa_chatbot_cn/dev/Dockerfile.dev:
--------------------------------------------------------------------------------
1 | FROM python:3.6-slim
2 |
3 | SHELL ["/bin/bash", "-c"]
4 |
5 | # Default to UTF-8 file.encoding
6 | ENV LANG C.UTF-8
7 |
8 | RUN apt-get update -qq && \
9 | apt-get install -y --no-install-recommends \
10 | apt-utils \
11 | vim \
12 | procps \
13 | build-essential \
14 | wget \
15 | unzip \
16 | openssh-client \
17 | graphviz-dev \
18 | pkg-config \
19 | git-core \
20 | openssl \
21 | libssl-dev \
22 | libffi6 \
23 | libffi-dev \
24 | libpng-dev \
25 | curl \
26 | python3-tk \
27 | tk-dev && \
28 | apt-get clean && \
29 | rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \
30 | mkdir /app
31 |
32 | WORKDIR /app
33 |
34 | ENV PYTHONPATH "${PYTHONPATH}:/app"
35 |
36 | COPY requirements.txt ./
37 |
38 | RUN pip install --upgrade pip -i https://mirrors.aliyun.com/pypi/simple/ && \
39 | pip install --no-cache-dir -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/
40 |
41 | ENTRYPOINT ["/bin/sh"]
42 | CMD ["/app/dev/run.sh"]
43 |
--------------------------------------------------------------------------------
/rasa_chatbot_cn/dev/deploy_dev.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | if [ ! "$(docker ps -a | grep mongo)" ]
3 | then echo "start mongo db docker";
4 | mkdir ~/data;
5 | docker pull mongo;
6 | docker run --name mongo -p 27017:27017 -v ~/data:/data/db -itd mongo;
7 | fi
8 |
9 | CHATBOT_VERSION=0.0.1
10 |
11 | docker build -f dev/Dockerfile.dev . -t chatbot_dev:$CHATBOT_VERSION
12 | docker run --name chatbot_dev -p 5005:5005 -p 5002:5002 --link mongo -v "$PWD":/app -itd chatbot_dev:$CHATBOT_VERSION
13 |
--------------------------------------------------------------------------------
/rasa_chatbot_cn/dev/run.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | make train
4 | make run
5 |
--------------------------------------------------------------------------------
/rasa_chatbot_cn/domain.yml:
--------------------------------------------------------------------------------
1 | slots:
2 | item:
3 | type: text
4 | time:
5 | type: text
6 | phone_number:
7 | type: text
8 | price:
9 | type: text
10 | package:
11 | type: text
12 |
13 | intents:
14 | - greet
15 | - confirm
16 | - goodbye
17 | - thanks
18 | - inform
19 | - request_management
20 | - request_search
21 | - deny
22 | - unknown_intent
23 |
24 | entities:
25 | - item
26 | - time
27 | - phone_number
28 | - price
29 | - package
30 |
31 | templates:
32 | utter_greet:
33 | - text: "您好! 我是机器人小贝,很高兴为您服务。"
34 | - text: "你好! 我是小贝,可以帮您办理流量套餐,话费查询等业务。"
35 | - text: "hi! 我是小贝,有什么可以帮您吗。"
36 | utter_goodbye:
37 | - text: "再见,为您服务很开心"
38 | - text: "Bye, 下次再见"
39 | utter_default:
40 | - text: "您说什么,小贝听不懂。"
41 | - text: "你说的小贝不懂呀!"
42 | - text: "不好意思,您能换个说法吗?"
43 | utter_thanks:
44 | - text: "不用谢"
45 | - text: "我应该做的"
46 | - text: "您开心我就开心"
47 | utter_ask_morehelp:
48 | - text: "还有什么能帮您吗?"
49 | - text: "您还需要什么服务?"
50 | utter_ask_time:
51 | - text: "你想查哪个时间段的?"
52 | - text: "你想查几月份的?"
53 | utter_ask_phonenum:
54 | - text: "你想查的手机号码是?"
55 | utter_confirm:
56 | - text: "你确认吗?"
57 | utter_ask_package:
58 | - text: "我们现在支持办理流量套餐:套餐一:二十元包月三十兆;套餐二:四十元包月八十兆,请问您需要哪个?"
59 | - text: "我们有如下套餐供您选择:套餐一:二十元包月三十兆;套餐二:四十元包月八十兆,请问您需要哪个?"
60 | utter_ack_management:
61 | - text: "已经为您办理好了{package}"
62 |
63 | actions:
64 | - utter_greet
65 | - utter_goodbye
66 | - utter_default
67 | - utter_thanks
68 | - utter_ask_morehelp
69 | - utter_ask_time
70 | - utter_ask_package
71 | - utter_ack_management
72 | - utter_ask_phonenum
73 | - utter_confirm
74 | - action_search_consume
75 |
--------------------------------------------------------------------------------
/rasa_chatbot_cn/process.py:
--------------------------------------------------------------------------------
1 | import urllib3
2 | import json
3 | import os
4 | import logging
5 |
6 |
7 | class RasaIntent:
8 | """
9 | @基于rasa的通用intent意图识别
10 | @参考https://github.com/bigbrother666sh/Awada
11 | """
12 | def __init__(
13 | self,
14 | logs: str = '.utils',
15 | port: str = '5005',
16 | ) -> None:
17 | # 1. create the cache_dir
18 | self.cache_dir = logs
19 | os.makedirs(self.cache_dir, exist_ok=True)
20 |
21 | # 2. save the log info into .log file
22 | log_formatter = logging.Formatter(fmt='%(levelname)s - %(message)s')
23 | self.logger = logging.getLogger('rasaintent')
24 | self.logger.handlers = []
25 | self.logger.setLevel('INFO')
26 | self.logger.propagate = False
27 | log_file = os.path.join(self.cache_dir, 'intent_LTE.log')
28 |
29 | file_handler = logging.FileHandler(log_file, 'a', encoding='utf-8')
30 | file_handler.setLevel('INFO')
31 | file_handler.setFormatter(log_formatter)
32 | self.logger.addHandler(file_handler)
33 |
34 | # 3. create the http client
35 | self.http = urllib3.PoolManager()
36 |
37 | # 4. create the rasa url
38 | self.url = 'http://localhost:' + port + '/parse'
39 |
40 | # 3. create the http client
41 | self.rasa_url = 'http://localhost:'+port+'/model/parse'
42 | self.http = urllib3.PoolManager()
43 |
44 | _test_data = {'text': '我想邀请你一起投资'}
45 | _encoded_data = json.dumps(_test_data)
46 | _test_res = self.http.request('POST', self.rasa_url, body=_encoded_data)
47 | _result = json.loads(_test_res.data)
48 |
49 | if not _result:
50 | raise RuntimeError('Rasa server not running, pls start it first and trans the right port in str')
51 |
52 | def predict(self, text: str):
53 | _test_data = {'text': text}
54 | _encoded_data = json.dumps(_test_data)
55 | _test_res = self.http.request('POST', self.rasa_url, body=_encoded_data)
56 | _result = json.loads(_test_res.data)
57 | _intent = _result['intent']['name']
58 | _conf = _result['intent']['confidence']
59 | if _conf >= 0.5 and _intent != 'nlu_fallback':
60 | self.logger.info(f'text: {text}---Intent: {_intent} confidence: {_conf}')
61 | else:
62 | self.logger.warning(f'text: {text}---Intent: {_intent} confidence: {_conf}')
63 | return _intent, _conf
64 |
65 |
66 | if __name__ == "__main__":
67 | nlu_intent = RasaIntent()
68 | print("====意图测试====")
69 |
70 | while True:
71 | print("输入Q退出")
72 | prompt = input("输入待测文本:")
73 | if prompt.lower() == "q":
74 | break
75 | intent, conf = nlu_intent.predict(prompt)
76 | print("检测出意图:", intent)
77 | print("意图置信度:", conf)
--------------------------------------------------------------------------------
/rooms.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/rooms.json
--------------------------------------------------------------------------------
/script/源1.0预训练语言模型使用示例V1.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/script/源1.0预训练语言模型使用示例V1.pdf
--------------------------------------------------------------------------------
/script/源APIExp使用手册0510.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Turing-Project/AntiFraudChatBot/a912198e2b23184709e22a6cdd049f8f965d2aa8/script/源APIExp使用手册0510.pdf
--------------------------------------------------------------------------------
/test.py:
--------------------------------------------------------------------------------
1 | """
2 | Python Wechaty - https://github.com/wechaty/python-wechaty
3 | Authors: Huan LI (李卓桓)
4 | Jingjing WU (吴京京)
5 | 2020 @ Copyright Wechaty Contributors
6 | Licensed under the Apache License, Version 2.0 (the 'License');
7 | you may not use this file except in compliance with the License.
8 |
9 | You may obtain a copy of the License athttp://www.apache.org/licenses/LICENSE-2.0
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an 'AS IS' BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and limitations under the License.
14 | """
15 | import os
16 | import asyncio
17 | import subprocess
18 |
19 | from urllib.parse import quote
20 |
21 | from wechaty import (
22 | Contact,
23 | FileBox,
24 | Message,
25 | Wechaty,
26 | ScanStatus,
27 | )
28 |
29 |
30 | async def on_message(msg: Message):
31 | """
32 | Message Handler for the Bot
33 | """
34 | if msg.text() == 'ding':
35 | await msg.say('dong')
36 |
37 | file_box = FileBox.from_url(
38 | 'https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/'
39 | 'u=1116676390,2305043183&fm=26&gp=0.jpg',
40 | name='ding-dong.jpg'
41 | )
42 | try:
43 | await msg.say(file_box)
44 | except Exception as e:
45 | print("catch some errors:", e)
46 |
47 |
48 | async def on_scan(
49 | qrcode: str,
50 | status: ScanStatus,
51 | _data,
52 | ):
53 | """
54 | Scan Handler for the Bot
55 | """
56 | print('Status: ' + str(status))
57 | print('View QR Code Online: https://wechaty.js.org/qrcode/' + quote(qrcode))
58 |
59 |
60 | async def on_login(user: Contact):
61 | """
62 | Login Handler for the Bot
63 | """
64 | print(user)
65 | # TODO: To be written
66 |
67 |
68 | async def main():
69 | """
70 | Async Main Entry
71 | """
72 | #
73 | # Make sure we have set WECHATY_PUPPET_SERVICE_TOKEN in the environment variables.
74 | # Learn more about services (and TOKEN) from https://wechaty.js.org/docs/puppet-services/
75 | #
76 | # It is highly recommanded to use token like [paimon] and [wxwork].
77 | # Those types of puppet_service are supported natively.
78 | # https://wechaty.js.org/docs/puppet-services/paimon
79 | # https://wechaty.js.org/docs/puppet-services/wxwork
80 | #
81 | # Replace your token here and umcommt that line, you can just run this python file successfully!
82 | # os.environ['token'] = 'puppet_paimon_your_token'
83 | # os.environ['token'] = 'puppet_wxwork_your_token'
84 | #
85 | if 'WECHATY_PUPPET_SERVICE_TOKEN' not in os.environ:
86 | print('''
87 | Error: WECHATY_PUPPET_SERVICE_TOKEN is not found in the environment variables
88 | You need a TOKEN to run the Python Wechaty. Please goto our README for details
89 | https://github.com/wechaty/python-wechaty-getting-started/#wechaty_puppet_service_token
90 | ''')
91 |
92 | #for cloud-service
93 | os.environ['WECHATY_PUPPET_SERVICE_TOKEN']='puppet_padlocal_3f904c6aa9cd4c548944f6688a829707'
94 | os.environ['WECHATY_PUPPET']='wechaty-puppet-padlocal'
95 | os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT']='43.154.97.162:8788'
96 |
97 | # export WECHATY_PUPPET=wechaty-puppet-padlocal
98 | # export WECHATY_PUPPET_PADLOCAL_TOKEN=puppet_padlocal_3f904c6aa9cd4c548944f6688a829707
99 | # export WECHATY_PUPPET_SERVER_PORT=8788
100 | # export WECHATY_LOG=verbose
101 |
102 | #for local-service
103 | # os.environ['WECHATY_PUPPET_SERVICE_TOKEN']='test01'
104 | # os.environ['WECHATY_PUPPET']='wechaty-puppet-xp'
105 | # os.environ['WECHATY_PUPPET_SERVICE_ENDPOINT']='127.0.0.1:8080'
106 |
107 | # os.environ['WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_SERVER']='true'
108 | # os.environ['WECHATY_PUPPET_SERVICE_NO_TLS_INSECURE_CLIENT']='true'
109 |
110 | bot = Wechaty()
111 |
112 | bot.on('scan', on_scan)
113 | bot.on('login', on_login)
114 | bot.on('message', on_message)
115 |
116 | await bot.start()
117 |
118 | print('[Python Wechaty] Ding Dong Bot started.')
119 |
120 |
121 | asyncio.run(main())
--------------------------------------------------------------------------------
/url_config.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import hashlib
3 | import time
4 | import json
5 | import os
6 |
7 | ACCOUNT = ''
8 | PHONE = ''
9 |
10 | # SUBMIT_URL = "http://api-air.inspur.com:32102/v1/interface/api/requestId?"
11 | # REPLY_URL = "http://api-air.inspur.com:32102/v1/interface/api/result?"
12 |
13 | SUBMIT_URL = "http://api-air.inspur.com:32102/v1/interface/api/infer/getRequestId?"
14 | REPLY_URL = "http://api-air.inspur.com:32102/v1/interface/api/result?"
15 |
16 | def code_md5(str):
17 | code=str.encode("utf-8")
18 | m = hashlib.md5()
19 | m.update(code)
20 | result= m.hexdigest()
21 | return result
22 |
23 | def rest_get(url, header,timeout, show_error=False):
24 | '''Call rest get method'''
25 | try:
26 | response = requests.get(url, headers=header,timeout=timeout, verify=False)
27 | return response
28 | except Exception as exception:
29 | if show_error:
30 | print(exception)
31 | return None
32 |
33 | def header_generation():
34 | """Generate header for API request."""
35 | t=time.strftime("%Y-%m-%d", time.localtime())
36 | global ACCOUNT, PHONE
37 | ACCOUNT, PHONE = os.environ.get('YUAN_ACCOUNT').split('||')
38 | token=code_md5(ACCOUNT+PHONE+t)
39 | headers = {'token': token}
40 | return headers
41 |
42 | def submit_request(query,temperature,topP,topK,max_tokens,engine):
43 | """Submit query to the backend server and get requestID."""
44 | headers=header_generation()
45 | # url=SUBMIT_URL + "account={0}&data={1}&temperature={2}&topP={3}&topK={4}&tokensToGenerate={5}&type={6}".format(ACCOUNT,query,temperature,topP,topK,max_tokens,"api")
46 | url=SUBMIT_URL + "engine={0}&account={1}&data={2}&temperature={3}&topP={4}&topK={5}&tokensToGenerate={6}" \
47 | "&type={7}".format(engine,ACCOUNT,query,temperature,topP,topK, max_tokens,"api")
48 | #print(url)
49 | response=rest_get(url,headers,30)
50 | #print(response)
51 | response_text = json.loads(response.text)
52 | if response_text["flag"]:
53 | requestId = response_text["resData"]
54 | return requestId
55 | else:
56 | raise RuntimeWarning(response_text)
57 |
58 | def reply_request(requestId,cycle_count=5):
59 | """Check reply API to get the inference response."""
60 | url = REPLY_URL + "account={0}&requestId={1}".format(ACCOUNT, requestId)
61 | headers=header_generation()
62 | for i in range(cycle_count):
63 | response = rest_get(url, headers, 30, show_error=True)
64 | response_text = json.loads(response.text)
65 | if response_text["resData"] != None:
66 | return response_text
67 | if response_text["flag"] == False and i ==cycle_count-1:
68 | raise RuntimeWarning(response_text)
69 | time.sleep(3)
70 |
71 |
--------------------------------------------------------------------------------