├── .gitignore
├── .idea
├── .gitignore
├── inspectionProfiles
│ └── profiles_settings.xml
├── misc.xml
├── modules.xml
├── teelebot-master.iml
└── vcs.xml
├── README.md
├── plugins
├── Menu
│ ├── Menu.py
│ ├── Menu_screenshot.png
│ ├── __init__.py
│ └── config.ini
├── __init__.py
├── add_code
│ ├── __init__.py
│ └── add_code.py
├── deluser
│ ├── __init__.py
│ ├── deluser.py
│ └── usertext.json
├── invite_code
│ ├── __init__.py
│ ├── code.txt
│ ├── invite_code.py
│ └── usertext.json
├── len_invite
│ ├── __init__.py
│ └── len_invite.py
└── updatacode
│ ├── __init__.py
│ ├── dlercloud.py
│ └── updatacode.py
├── setup.py
├── supervisor.conf
└── teelebot
├── __init__.py
├── __main__.py
├── handler.py
├── logger.py
├── plugins
├── About
│ ├── About.py
│ ├── __init__.py
│ └── icon.png
├── Chat
│ ├── Chat.py
│ ├── __init__.py
│ └── hello.ogg
├── Hello
│ ├── Hello.py
│ ├── __init__.py
│ └── helloworld.png
├── Menu
│ ├── Menu.py
│ └── __init__.py
├── PluginCTL
│ ├── PluginCTL.py
│ └── __init__.py
├── Schedule
│ ├── Schedule.py
│ └── __init__.py
├── Uptime
│ ├── Uptime.py
│ └── __init__.py
└── __init__.py
├── polling.py
├── request.py
├── schedule.py
├── teelebot.py
├── version.py
└── webhook.py
/.gitignore:
--------------------------------------------------------------------------------
1 | __pycache__/
2 | info.txt
3 | test.py
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /workspace.xml
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/teelebot-master.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 框架更新了我还没同步过来 大的结构会有变化 三天之内全部修改完 我说的 耶稣也拦不住我(更新完了)
2 |
3 | ## 新手安装教程请看
4 | https://southcat.net/2526/
5 |
6 | ## 插件必看
7 | 喵帕斯的已经没了 自动补码是喵帕斯的 需要dlercloud的请把updatacode目录下的dlercloud.py文件名修改为updatacode.py覆盖掉源文件,需要其他的自己修改或是可以找我
8 | 我有时间就做一个 马上放假了时间多的是
9 |
10 | ## 第一次开发有问题及时反馈
11 | email:admin@southcat.net
12 |
13 | 博客:[南猫](https://southcat.net)
14 |
15 | 基于[teelebot](https://github.com/plutobell/teelebot)开源项目开发。
16 |
17 | ## 增加功能
18 |
19 | 1.邀请码自动发码模块 并且限制领取数量(邀请码添加在invite_code目录下的code.txt 一行一个)
20 |
21 | 2.邀请码数量统计,如果邀请码用完自动给管理员发消息(需自行修改len_invite.py里面的发送id)
22 |
23 | 3.补码模块已经开发 指令`/add_code邀请码` 请注意一行一码因为太菜要求比较严格 可能效果不是很理想,请等我再去学两天python再回来改
24 |
25 | 4.删除用户信息模块,删除后用户可以再次获取邀请码指令`/del用户id`目前只能删除单个用户
26 |
27 | 5.updatacode模块,目前仅支持从喵帕斯进行获取,会自动抓取邀请码页面前两页的邀请码,并和之前的数据进行对比,然后写入code,后续会支持更多网站
28 | 理论上所有和喵帕斯同模板的都可以使用,请在updatacode/updatacode.py 文件夹内填入你的账号密码
29 |
30 | 6.很遗憾喵关门了 在plguins/updatacode文件夹下更新了dlercloud的自动更新模块,需要使用的话备份原文件,将文件名修改为updatacode.py即可,理论上喵帕斯的补码支持所有同模板的网站只需要更改里面的网站即可,接下来会针对所有有邀请码模块的网站开发补码模块,或是你们也可以从邮箱或是tg发给我网站,我尽量进行适配(放假闲的)
31 | ## 开发计划
32 | 1.增加管理员添加邀请码模块
33 |
34 | 2.多类邀请码模块支持
35 |
36 | 3.代码优化,目前存在大量多余的代码
37 |
38 | 4.添加白名单,白名单用户支持无限获取
39 |
40 | 环境要求
41 | Python版本
42 | teelebot 只支持 Python3.x,不支持Python2.x。
43 |
44 | 本项目在 Python 3.5 及以上版本测试通过。
45 |
46 | 安装
47 | pip install teelebot
48 | 升级
49 | pip install teelebot --upgrade
50 | 使用
51 | 一行命令启动 (Polling Mode)
52 | teelebot -c/--config -k/--key -r/--root
53 | 此命令会自动生成在Polling模式下适用的配置文件,但仍需手动配置插件路径。
54 |
55 | 一、运行模式
56 | teelebot 支持以 Webhook 模式和 Polling 模式运行。生产环境推荐使用 Webhook 模式,而 Polling 则仅用于开发环境。
57 |
58 | 1、Webhook 模式
59 | 要以 Webhook 模式运行,请将配置文件字段 webhook 设置为 True ,此模式涉及的配置文件字段如下:
60 |
61 | [config]
62 | webhook=True
63 | self_signed=False
64 | cert_key=your private cert path
65 | cert_pub=your public cert path
66 | server_address=your server ip address or domain
67 | server_port=your server port
68 | local_address=webhook local address
69 | local_port=webhook local port
70 | self_signed 用于设置是否使用自签名证书,而 cert_key 和 cert_pub 则是你的证书路径(绝对路径),server_address 为你的服务器公网IP, server_port 为服务器的端口(目前 telegram 官方仅支持 443, 80, 88, 8443),local_address 为Webhook 本地监听地址, local_port 为 Webhook 本地运行的端口。
71 |
72 | 推荐搭配 nginx 使用,自签名证书生成请参考:Generating a self-signed certificate pair (PEM)
73 |
74 | 2、Polling 模式
75 | 要以 Polling 模式运行,只需要保证配置文件 webhook 字段为 False 即可。此模式最基本的配置文件如下:
76 |
77 | [config]
78 | key=bot key
79 | pool_size=40
80 | webhook=False
81 | root_id=your user id
82 | debug=False
83 | plugin_dir=your plugin dir
84 | 二、运行
85 | 任意路径打开终端,输入以下命令:
86 |
87 | 对于使用程序配置文件默认路径的:
88 |
89 | 输入teelebot 回车,正常情况下你应该能看见屏幕提示机器人开始运行。
90 |
91 | 对于命令行手动指定配置文件路径的:
92 |
93 | 输入teelebot -c/--config 回车,正常情况下你应该能看见屏幕提示机器人开始运行。(更多指令请通过 -h/--help 查看)
94 |
95 | 可配合supervisor使用。
96 |
97 | 三、配置文件
98 | 完整的配置文件如下所示:
99 |
100 | [config]
101 | key=bot key
102 | plugin_dir=your plugin dir
103 | pool_size=40 //the thread pool size, default 40, range(1, 101)
104 | webhook=False
105 | self_signed=False //Optional while webhook is False
106 | cert_key=your private cert path //Optional while webhook is False
107 | cert_pub=your public cert path //Optional while webhook is False
108 | server_ip=your server ip address //Optional while webhook is False
109 | server_port=your server port //Optional while webhook is False
110 | local_address=webhook local address //Optional while webhook is False
111 | local_port=webhook local port //Optional while webhook is False
112 | root_id=your user id
113 | debug=False
114 | drop_pending_updates=False
115 | local_api_server=local api server address //[Optional]
116 | 在 1.13.0 及以上版本,支持自动生成配置文件。(默认为Polling模式)
117 |
118 | 1.在命令行未指定配置文件路径的情况下,会在默认配置文件路径下不存在配置文件时自动生成配置文件 config.cfg。
119 |
120 | 在Linux下,会自动在用户目录下创建文件夹 .teelebot ,并生成配置文件 config.cfg
121 |
122 | 在Windows下,则会在 C:\Users\ 目录下创建文件夹 .teelebot ,并生成配置文件 config.cfg
123 |
124 | 2.指定配置文件
125 |
126 | Linux 和 Windows 都可在命令行通过参数手动指定配置文件路径,命令格式:
127 |
128 | teelebot -c/--config
129 | 路径必须为绝对路径,此情况下也会在指定路径上不存在配置文件时自动生成配置文件 ,配置文件命名由指定的路径决定。
130 |
131 | Tip: 自动生成的配置文件未设置这几个字段值:key、root_id、plugin_dir,key 和 root_id 为必须,但我们仍然可以通过命令行设置他们:
132 |
133 | teelebot -c/--config -k/--key -r/--root
134 | 使用以上命令会以Polling模式运行框架,而无需困扰于处理配置文件。
135 |
136 | 之后请手动设置 plugin_dir 。
137 |
138 | 插件开发指南 (以 Hello 插件为例) BETA 0.8
139 | 一、插件结构
140 | 一个完整的 teelebot 插件应当呈现为一个文件夹,即一个Python包,以 Hello 插件为例,最基本的目录结构如下:
141 |
142 | Hello/
143 | ./__init__.py
144 | ./Hello.py
145 | ./Hello_screenshot.png
146 | ./readme.md
147 | ./requirement.txt
148 | 二、规则
149 | 命名
150 | 在构建teelebot插件中应当遵守的规则是:每个插件目录下应当存在一个与插件同名的.py 文件,比如插件 Hello 中的 Hello.py 文件,并且此文件中必须存在作为插件入口的同名函数,以插件 Hello 为例:
151 |
152 | #file Hello/Hello.py
153 |
154 | # -*- coding:utf-8 -*-
155 |
156 | def Hello(bot, message):
157 | pass
158 | 函数 Hello() 即为插件的入口函数,参数 bot 为Bot接口库实例化对象,参数 message 用于接收消息数据。
159 |
160 | 资源路径
161 | 若要打开某个插件目录下的文件资源,需要使用的路径应当遵循以下的格式:
162 |
163 | bot.path_converter(bot.plugin_dir + "/")
164 | 方法 path_converter 根据操作系统转换路径格式。
165 |
166 | 三、自定义触发指令
167 | 插件指令
168 | 插件的触发指令可不同于插件名,允许自定义。以插件 Hello 为例,触发指令为 /helloworld 而不是 Hello。
169 |
170 | 修改插件目录下的 __init__.py 文件设置触发指令:
171 |
172 | #file Hello/__init__.py
173 |
174 | #/helloworld
175 | #Hello World插件例子
176 | 第一行为触发指令,默认以 / 作为前缀;第二行为插件简介。
177 |
178 | 不用作插件的特殊情况
179 | 通常情况下,位于 plugins 目录下的所有包都将被识别为插件并自动加载到 teelebot 中。但在某些情况下,存在并不用作插件而只是多个插件共用包的情况,若想该包不被 teelebot 加载,请将触发指令设置为 ~~ 。以 tools 共用包为例, __init__.py 文件内容如下:
180 |
181 | #fille tools/__init__.py
182 |
183 | #~~
184 | #tools 包的简介
185 | 建议用作插件的包名遵守 Pascal命名法,即每个单词的首字母大写;而不用做插件的包名使用全小写的包名,每个单词之间以_ 分隔。以区分 插件包 和 非插件包 :
186 |
187 | - plugins
188 | - Menu #插件包
189 | - tools #非插件包
190 | 四、插件模板创建工具
191 | 在 v1.9.20_dev 及以上版本,可以通过命令行指令一键创建插件模板。
192 |
193 | teelebot -p/--plugin
194 | 该指令会使用框架配置文件(config.cfg)中的插件路径作为所创建插件模板的存放路径。
195 |
196 | 五、周期性任务
197 | 在 v1.11.1 及以上版本,可以创建周期性任务,功能类似循环定时器。
198 |
199 | 可获得的方法:
200 |
201 | schedule.add : 添加任务
202 | schedule.delete : 移除任务
203 | schedule.find : 查找任务
204 | schedule.clear : 清空任务池
205 | schedule.status : 查看任务池状态
206 | 例:
207 |
208 | ok, uid = bot.schedule.add(gap, event, (bot, ))
209 | ok, uid = bot.schedule.delete(uid)
210 | ok, uid = bot.schedule.find(uid)
211 | ok, uid = bot.schedule.clear()
212 | ok, uid = bot.schedule.status()
213 | 周期性任务池的大小为全局线程池的三分之一 ,线程池大小则可通过配置文件指定。
214 | 1.克隆或点击下载本项目到本地,保证本机安装有`Python3.x`版本和包`requests` ;
215 |
216 |
217 |
218 | 2.`config.cfg` 配置文件
219 |
220 | 配置文件格式:
221 |
222 | ```python
223 | [config]
224 | key=your key
225 | pool_size=40 //the thread pool size, default 40, range(1, 101)
226 | webhook=False
227 | cert_pub=your public certificate dir //Optional while webhook is False
228 | server_ip=your server ip address //Optional while webhook is False
229 | server_port=your server port //Optional while webhook is False
230 | local_address=webhook local address //Optional while webhook is False
231 | local_port=webhook local port //Optional while webhook is False
232 | root=your user id
233 | debug=False
234 | timeout=60
235 | plugin_dir=your plugin dir //[Optional]
236 | ```
237 |
238 | * Linux
239 |
240 | 在 `/root` 目录下创建文件夹 `.teelebot` ,并在其内新建配置文件 `config.cfg` ,按照上面的格式填写配置文件
241 |
242 | * Windows
243 |
244 | 在 `C:\Users\` 目录下创建文件夹 `.teelebot` ,并在其内新建配置文件 `config.cfg` ,按照上面的格式填写配置文件
245 |
246 | * 指定配置文件
247 |
248 | Linux 和 Windows 都可在命令行通过参数手动指定配置文件路径,命令格式:
249 |
250 | ```
251 | python -m teelebot -c/-C
252 | ```
253 |
254 | 路径必须为绝对路径。
255 |
256 |
257 |
258 | 3.运行
259 |
260 | 终端下进入teelebot文件夹所在目录。
261 |
262 | * 对于使用程序配置文件默认路径的:
263 |
264 | 输入`python -m teelebot` 回车,正常情况下你应该能看见屏幕提示机器人开始运行。
265 |
266 | * 对于命令行手动指定配置文件路径的:
267 |
268 | 输入`python -m teelebot -c/-C ` 回车,正常情况下你应该能看见屏幕提示机器人开始运行。
269 |
270 |
271 |
272 | #### 三、Pip安装运行
273 |
274 | ##### 安装 #####
275 |
276 | * 确保本机Python环境拥有pip包管理工具。
277 |
278 | * 在本项目Releases页面下载包文件。
279 |
280 | * 本机命令行进入包文件所在目录,执行:
281 |
282 | ```
283 | pip install
284 |
285 | or
286 |
287 | pip3 install
288 | ```
289 |
290 | 由于API未封装完毕,暂未上传至 `PyPI` ,故不能在线安装,望谅解。
291 |
292 | ##### 运行 #####
293 |
294 | 任意路径打开终端,输入以下命令:
295 |
296 | - 对于使用程序配置文件默认路径的:
297 |
298 | 输入`teelebot` 回车,正常情况下你应该能看见屏幕提示机器人开始运行。
299 |
300 | - 对于命令行手动指定配置文件路径的:
301 |
302 | 输入`teelebot -c/-C ` 回车,正常情况下你应该能看见屏幕提示机器人开始运行。
303 |
304 |
305 |
306 | 可配合`supervisor`使用。
307 |
308 |
309 |
310 |
311 |
312 | ## 插件开发指南 (以 Hello 插件为例) BETA 0.6
313 |
314 | #### 一、插件结构
315 |
316 | 一个完整的 `teelebot` 插件应当呈现为一个文件夹,即一个Python包,以 `Hello` 插件为例,最基本的目录结构如下:
317 |
318 | ```Python
319 | Hello/
320 | ./__init__.py
321 | ./Hello.py
322 | ./Hello_screenshot.png
323 | ./readme.md
324 | ```
325 |
326 | #### 二、规则
327 |
328 | ##### 命名
329 |
330 | 在构建teelebot插件中应当遵守的规则是:每个插件目录下应当存在一个与插件同名的`.py` 文件,比如插件 `Hello ` 中的 `Hello.py` 文件,并且此文件中必须存在作为插件入口的同名函数,以插件 `Hello` 为例:
331 |
332 | ```python
333 | #file Hello/Hello.py
334 |
335 | # -*- coding:utf-8 -*-
336 |
337 | def Hello(bot, message):
338 | pass
339 | ```
340 |
341 | 函数 `Hello()` 即为插件的入口函数,参数 `bot` 为Bot接口库实例化对象,参数 `message` 用于接收消息数据。
342 |
343 |
344 |
345 |
346 |
347 | ##### 资源路径
348 |
349 | 若要打开某个插件目录下的文件资源,需要使用的路径应当遵循以下的格式:
350 |
351 | ```python
352 | bot.plugin_dir + "/"
353 | ```
354 |
355 | #### 三、自定义触发指令
356 |
357 | ##### 插件指令
358 |
359 | 插件的触发指令可不同于插件名,允许自定义。以插件 `Hello` 为例,触发指令为 `/helloworld` 而不是 `Hello`。
360 |
361 | 修改插件目录下的 `__init__.py` 文件设置触发指令:
362 |
363 | ```python
364 | #file Hello/__init__.py
365 |
366 | #/helloworld
367 | #Hello World插件例子
368 | ```
369 |
370 | 第一行为触发指令,默认以 `/` 作为前缀;第二行为插件简介。
371 |
372 |
373 |
374 | ##### 不用作插件的特殊情况
375 |
376 | 通常情况下,位于 `plugins` 目录下的所有包都将被识别为插件并自动加载到 `teelebot` 中。但在某些情况下,存在并不用作插件而只是多个插件共用包的情况,若想该包不被 `teelebot` 加载,请将触发指令设置为 `~~` 。以 `tools` 共用包为例, `__init__.py` 文件内容如下:
377 |
378 | ```python
379 | #fille tools/__init__.py
380 |
381 | #~~
382 | #tools 包的简介
383 | ```
384 |
385 | 建议用作插件的包名遵守 `Pascal命名法`,即每个单词的首字母大写;而不用做插件的包名使用全小写的包名,每个单词之间以`_` 分隔。以区分 `插件包` 和 `非插件包` :
386 |
387 | ```python
388 | - plugins
389 | - Menu #插件包
390 | - tools #非插件包
391 | ```
392 |
--------------------------------------------------------------------------------
/plugins/Menu/Menu.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | '''
3 | creation time: 2019-8-15
4 | last_modify: 2020-11-16
5 | '''
6 | import os
7 |
8 | def Menu(bot, message):
9 | chat_id = message["chat"]["id"]
10 | message_id = message["message_id"]
11 | chat_type = message["chat"]["type"]
12 |
13 | prefix = "start"
14 |
15 | plugin_bridge = bot.plugin_bridge
16 | plugin_dir = bot.plugin_dir
17 | plugin_list = list(plugin_bridge.keys())
18 |
19 | if chat_type != "private" and "/pluginctl" in plugin_bridge.values() and plugin_bridge["PluginCTL"] == "/pluginctl":
20 | if os.path.exists(bot.path_converter(plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db")):
21 | with open(bot.path_converter(plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db"), "r") as f:
22 | plugin_setting = f.read().strip()
23 | plugin_list_off = plugin_setting.split(',')
24 | plugin_list_temp = []
25 | for plugin in plugin_bridge.keys():
26 | if plugin not in plugin_list_off:
27 | plugin_list_temp.append(plugin)
28 | plugin_list = plugin_list_temp
29 |
30 | plugin_count = len(plugin_list)
31 | page_size = 5
32 | page_total = int((plugin_count + page_size - 1) / page_size) # 总页数=(总数+每页数量-1)/每页数量
33 | page_callback_command = "/" + prefix + "page?page="
34 |
35 | if not os.path.exists(bot.path_converter(plugin_dir + "Menu/config.ini")):
36 | first_btn = ["交流群组", "https://t.me/teelebot_chat"]
37 | last_btn = ["项目地址", "https://github.com/plutobell/teelebot"]
38 | else:
39 | with open(bot.path_converter(plugin_dir + "Menu/config.ini"), 'r') as g:
40 | first_btn = g.readline().strip().split(',')
41 | last_btn = g.readline().strip().split(',')
42 |
43 | wait_time = plugin_count * 7
44 |
45 | if "reply_markup" in message.keys():
46 | click_user_id = message["click_user"]["id"]
47 | from_user_id = message["reply_to_message"]["from"]["id"]
48 | callback_query_data = message["callback_query_data"]
49 |
50 | if callback_query_data[:len(page_callback_command)] == page_callback_command:
51 | if click_user_id == from_user_id:
52 | page = int(callback_query_data.split('=')[1])
53 | page, menu_str = menu_text(bot, plugin_dir=plugin_dir, page=page, page_total=page_total, page_size=page_size, plugin_list=plugin_list)
54 | previous_page = page - 1
55 | if previous_page < 1:
56 | previous_page = 1
57 | next_page = page + 1
58 | if next_page > page_total:
59 | next_page = page_total
60 |
61 | if page_total == 1:
62 | inlineKeyboard = [
63 | [
64 | {"text": first_btn[0], "url": first_btn[1]},
65 | {"text": last_btn[0], "url": last_btn[1]},
66 | ]
67 | ]
68 | elif page == 1:
69 | inlineKeyboard = [
70 | [
71 | {"text": first_btn[0], "url": first_btn[1]},
72 | {"text": "下一页", "callback_data": page_callback_command + str(page+1)},
73 | ]
74 | ]
75 | elif page == page_total:
76 | inlineKeyboard = [
77 | [
78 | {"text": "上一页", "callback_data": page_callback_command + str(page-1)},
79 | {"text": last_btn[0], "url": last_btn[1]},
80 | ]
81 | ]
82 | else:
83 | inlineKeyboard = [
84 | [
85 | {"text": "上一页", "callback_data": page_callback_command + str(previous_page)},
86 | {"text": "下一页", "callback_data": page_callback_command + str(next_page)},
87 | ]
88 | ]
89 | reply_markup = {
90 | "inline_keyboard": inlineKeyboard
91 | }
92 | status = bot.editMessageText(chat_id=chat_id, message_id=message_id, text=menu_str, parse_mode="HTML", reply_markup=reply_markup)
93 | status = bot.answerCallbackQuery(message["callback_query_id"])
94 | else:
95 | status = bot.answerCallbackQuery(message["callback_query_id"], text="点啥点,关你啥事?", show_alert=bool("true"))
96 | else:
97 | page = 1
98 | if page_total == 1:
99 | inlineKeyboard = [
100 | [
101 | {"text": first_btn[0], "url": first_btn[1]},
102 | {"text": last_btn[0], "url": last_btn[1]},
103 | ]
104 | ]
105 | else:
106 | inlineKeyboard = [
107 | [
108 | {"text": first_btn[0], "url": first_btn[1]},
109 | {"text": "下一页", "callback_data": page_callback_command + str(page+1)},
110 | ]
111 | ]
112 | reply_markup = {
113 | "inline_keyboard": inlineKeyboard
114 | }
115 |
116 | page, menu_str = menu_text(bot=bot, plugin_dir=plugin_dir, page=page, page_total=page_total, page_size=page_size, plugin_list=plugin_list)
117 |
118 | status = bot.sendChatAction(chat_id, "typing")
119 | status = bot.sendMessage(chat_id=chat_id, text=menu_str, parse_mode="HTML", reply_to_message_id=message_id, reply_markup=reply_markup)
120 |
121 | bot.message_deletor(wait_time, message["chat"]["id"], status["message_id"])
122 |
123 | def menu_text(bot, plugin_dir, page, page_total, page_size, plugin_list):
124 | VERSION = bot.version
125 | if page < 1:
126 | page = 1
127 | elif page > page_total:
128 | page = page_total
129 |
130 | if page >=1 and page <= page_total:
131 | menu_str = ""
132 | plugin_range = range(page*page_size-page_size, page*page_size-1+1)
133 | for i, plugin in enumerate(plugin_list): #(now_page*page_size-page_size,now_page*page_size-1)
134 | if i in plugin_range:
135 | with open(bot.path_converter(plugin_dir + plugin + r"/__init__.py"), encoding="utf-8") as f:
136 | line_1 = ""
137 | line_2 = ""
138 | for i in range(2):
139 | if i == 0:
140 | line_1 = f.readline().strip()[1:]
141 | elif i == 1:
142 | line_2 = f.readline().strip()[1:]
143 | menu_str += "" + line_1 + " - " + line_2 + "\n\n"
144 | menu_str = "插件列表 [" + str(page) + "/" + str(page_total) + "]\n\n" + menu_str + "\nv" + VERSION + ""
145 |
146 | return page, menu_str
--------------------------------------------------------------------------------
/plugins/Menu/Menu_screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Everless321/telebot-invitecode/9ebbf77e85620485574b7bebd61d247e9a1d6612/plugins/Menu/Menu_screenshot.png
--------------------------------------------------------------------------------
/plugins/Menu/__init__.py:
--------------------------------------------------------------------------------
1 | #/start
2 | #Robot 插件列表
--------------------------------------------------------------------------------
/plugins/Menu/config.ini:
--------------------------------------------------------------------------------
1 | 南猫,https://southcat.net
2 | 开源地址,https://github.com/southcat/telebot-invitecode
3 |
4 | // inclues telegram links and ordinary links
--------------------------------------------------------------------------------
/plugins/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Everless321/telebot-invitecode/9ebbf77e85620485574b7bebd61d247e9a1d6612/plugins/__init__.py
--------------------------------------------------------------------------------
/plugins/add_code/__init__.py:
--------------------------------------------------------------------------------
1 | #/add_code
2 | #邀请码添加模块 请一行一码
--------------------------------------------------------------------------------
/plugins/add_code/add_code.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | import os
3 | import json
4 | global last_line
5 | global chongfu
6 | def add_code(bot, message):
7 | hx = "\n"
8 | chongfu = 0
9 | if str(message["from"]["id"]) == bot.root_id:
10 | with open(bot.plugin_dir + "add_code/__init__.py", encoding="utf-8") as f:
11 | h = f.readline()[1:]
12 | with open(bot.plugin_dir + 'invite_code/usertext.json', 'r') as f1:
13 | userjson = json.load(f1)
14 | if len(message["text"]) < len(h):
15 | status = bot.sendChatAction(message["chat"]["id"], "typing")
16 | status = bot.sendMessage(message["chat"]["id"], "添加失败,请输入邀请码", "HTML")
17 | return False
18 | code = message["text"][len(h) - 1:] + "\n"
19 | with open(bot.plugin_dir + 'invite_code/code.txt', 'r') as fp:
20 | lines = fp.readlines()
21 | last_line = lines[-1]
22 | if hx not in last_line:
23 | f = open(bot.plugin_dir + 'invite_code/code.txt', 'a+')
24 | f.write(hx)
25 | f.write(code)
26 | f.close()
27 | status = bot.sendMessage(message["chat"]["id"], "添加完成,当前邀请码剩余"+str(lentj(bot=bot)), "HTML")
28 | else:
29 | f = open(bot.plugin_dir + 'invite_code/code.txt', 'a+')
30 | f.write(code)
31 | f.close()
32 | status = bot.sendMessage(message["chat"]["id"], "添加完成,当前邀请码剩余"+str(lentj(bot=bot)), "HTML")
33 | else:
34 | status = bot.sendChatAction(message["chat"]["id"], "typing")
35 | status = bot.sendMessage(message["chat"]["id"], "添加失败,您没有权限添加", "HTML")
36 | def lentj(bot):
37 | count = 0
38 | for index, line in enumerate(open(bot.plugin_dir + 'invite_code/code.txt', 'r')):
39 | count += 1
40 | return count
41 |
--------------------------------------------------------------------------------
/plugins/deluser/__init__.py:
--------------------------------------------------------------------------------
1 | #/del
2 | #用于删除以获取邀请码的用户信息 /del用户ID
--------------------------------------------------------------------------------
/plugins/deluser/deluser.py:
--------------------------------------------------------------------------------
1 | import json
2 | import os
3 | def deluser(bot,message):
4 | hx = "\n"
5 | if str(message["from"]["id"]) == bot.root_id:
6 | with open(bot.plugin_dir + "deluser/__init__.py", encoding="utf-8") as f:
7 | h = f.readline()[1:]
8 | if len(message["text"]) < len(h):
9 | status = bot.sendChatAction(message["chat"]["id"], "typing")
10 | status = bot.sendMessage(message["chat"]["id"], "删除失败,请输入用户id", "HTML")
11 | return False
12 | userid = message["text"][len(h) - 1:]
13 | with open(bot.plugin_dir + 'invite_code/usertext.json', 'r') as f:
14 | userjson = json.load(f)
15 | if userid in userjson:
16 | del userjson[userid]
17 | with open(bot.plugin_dir + 'invite_code/usertext.json', 'w') as f1:
18 | json.dump(userjson, f1)
19 | status = bot.sendChatAction(message["chat"]["id"], "typing")
20 | status = bot.sendMessage(message["chat"]["id"], "删除用户信息成功", "HTML")
21 | else:
22 | status = bot.sendChatAction(message["chat"]["id"], "typing")
23 | status = bot.sendMessage(message["chat"]["id"], "该用户不存在", "HTML")
24 | else:
25 | status = bot.sendChatAction(message["chat"]["id"], "typing")
26 | status = bot.sendMessage(message["chat"]["id"], "删除失败,您没有权限", "HTML")
27 |
28 |
29 |
--------------------------------------------------------------------------------
/plugins/deluser/usertext.json:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Everless321/telebot-invitecode/9ebbf77e85620485574b7bebd61d247e9a1d6612/plugins/deluser/usertext.json
--------------------------------------------------------------------------------
/plugins/invite_code/__init__.py:
--------------------------------------------------------------------------------
1 | #/code
2 | #邀请码自动发码模块
--------------------------------------------------------------------------------
/plugins/invite_code/code.txt:
--------------------------------------------------------------------------------
1 | 123123
2 |
--------------------------------------------------------------------------------
/plugins/invite_code/invite_code.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 |
3 | import os
4 | import json
5 | tips = "有问题联系@southcat解决"
6 | def invite_code(bot, message):
7 | '''
8 | 通过读取usertext.json文件 来对比用户是否获取过,如果没有则从code.txt读取第一行的邀请码发送给用户,并删除这个邀请码。
9 | 邀请码为0时自动发送给管理员。
10 | '''
11 | sysl = lentj(bot=bot) #sysl=剩余数量
12 | with open(bot.plugin_dir + 'invite_code/usertext.json','r') as f: #打开文件给f
13 | userjson = json.load(f) #json.load读取f赋值给userjson
14 | status = bot.sendChatAction(message["chat"]["id"], "typing") #显示回复状态
15 | if yanzheng2(bot,message["from"]["id"]) == 0: #判断用户是否获取过
16 | status = bot.sendChatAction(message["chat"]["id"], "typing") #显示发送状态
17 | bot.sendMessage(message["chat"]["id"], "您已经获取过了哦", "HTML")#发送提示
18 | bot.sendMessage(message["chat"]["id"], tips, "HTML") #发送预设的提示
19 | else:
20 | if sysl == 0: #判断数量是否为0 比较容易理解
21 | #status = bot.sendMessage(512466300, "邀请码不足", "HTML") #邀请码不足提醒模块 如需开启请取消注释并将512466300修改为你的id
22 | status = bot.sendMessage(message["chat"]["id"], "邀请码数量不足请等待补充", "HTML") #发送信息
23 | else:
24 | invite_code1 = code(bot=bot) #函数赋值
25 | status = bot.sendChatAction(message["chat"]["id"], "typing") #显示发送状态
26 | bot.sendMessage(message["chat"]["id"], invite_code1, "HTML") #发送邀请码
27 | bot.sendMessage(message["chat"]["id"], tips, "HTML") #发送预设的提示
28 | userid = message["from"]["id"] #获取用户id
29 | userjson[userid] = invite_code1 #写入字典
30 | with open(bot.plugin_dir + 'invite_code/usertext.json','w') as f1: #打开文件
31 | json.dump(userjson,f1) #将修改过的字典写入文件
32 |
33 |
34 | def code(bot): #获取验证吗
35 | file = open(bot.plugin_dir + 'invite_code/code.txt','r')
36 | a = file.readline()
37 | lines = (i for i in open(bot.plugin_dir + 'invite_code/code.txt', 'r') if a not in i)
38 | f = open(bot.plugin_dir + 'invite_code/test_new.txt', 'w', encoding="utf-8")
39 | f.writelines(lines)
40 | f.close()
41 | os.rename(bot.plugin_dir + 'invite_code/code.txt', bot.plugin_dir + 'invite_code/test.bak')
42 | os.rename(bot.plugin_dir + 'invite_code/test_new.txt', bot.plugin_dir + 'invite_code/code.txt')
43 | os.remove(bot.plugin_dir + 'invite_code/test.bak')
44 | file.close()
45 | return a
46 |
47 | def lentj(bot): #验证码剩余数量统计
48 | count = 0
49 | for index, line in enumerate(open(bot.plugin_dir + 'invite_code/code.txt', 'r')):
50 | count += 1
51 | return count
52 |
53 | def yanzheng2(bot,user_id):
54 | with open(bot.plugin_dir + 'invite_code/usertext.json', 'r') as f:
55 | userjson = json.load(f)
56 | if str(user_id) in userjson.keys():
57 | return 0
58 | else:
59 | return 1
--------------------------------------------------------------------------------
/plugins/invite_code/usertext.json:
--------------------------------------------------------------------------------
1 | {"123123": "123123123","123123asd": "123123123"}
--------------------------------------------------------------------------------
/plugins/len_invite/__init__.py:
--------------------------------------------------------------------------------
1 | #/len_invite
2 | #统计邀请码数量
--------------------------------------------------------------------------------
/plugins/len_invite/len_invite.py:
--------------------------------------------------------------------------------
1 | import os
2 | import json
3 | def len_invite(bot,message):
4 | count_mps = lentj(bot=bot)
5 | with open(bot.plugin_dir + 'invite_code/usertext.json','r') as f: #打开文件给f
6 | userjson = json.load(f)
7 | status = bot.sendMessage(message["chat"]["id"], "邀请码剩余:"+str(lentj(bot=bot))+"\n"+"已发放用户:"+str(len(userjson)), "HTML")
8 | if count_mps == 0 :
9 | status= bot.sendMessage(512466300,"邀请码不足","HTML")
10 |
11 | def lentj(bot):
12 | count = 0
13 | for index, line in enumerate(open(bot.plugin_dir + 'invite_code/code.txt', 'r')):
14 | count += 1
15 | return count
--------------------------------------------------------------------------------
/plugins/updatacode/__init__.py:
--------------------------------------------------------------------------------
1 | #/updata
2 | #用于更新邀请码 请打开upadtacode.py填入你的账号信息
--------------------------------------------------------------------------------
/plugins/updatacode/dlercloud.py:
--------------------------------------------------------------------------------
1 | import requests
2 | # import re
3 | import json
4 | from bs4 import BeautifulSoup
5 |
6 | global codelist1
7 | codelist1 = []
8 | url = ["https://dlercloud.com/user/invite", "https://dlercloud.com/user/invite?page=2"]
9 |
10 | chongfu = 0
11 |
12 |
13 | def updatacode(bot,message):
14 | if str(message["from"]["id"]) == bot.root_id:
15 | for abc in url:
16 | a = shuaxin(bot,abc)
17 | if a == 1:
18 | status = bot.sendChatAction(message["chat"]["id"], "typing")
19 | bot.sendMessage(message["chat"]["id"], "更新失败,cookie失效或是遇到防火墙", "HTML")
20 | else:
21 | status = bot.sendChatAction(message["chat"]["id"], "typing")
22 | bot.sendMessage(message["chat"]["id"], "更新成功", "HTML")
23 | file = open(bot.plugin_dir + 'invite_code/code.txt', 'w+')
24 | for code1 in codelist1:
25 | if code1 not in file:
26 | file.write(str(code1))
27 | else:
28 | print(code1+"存在")
29 | pass
30 | codelist1.clear()
31 | else:
32 | status = bot.sendChatAction(message["chat"]["id"], "typing")
33 | bot.sendMessage(message["chat"]["id"], "您没有权限哦", "HTML")
34 | status = bot.sendChatAction(message["chat"]["id"], "typing")
35 | bot.sendMessage(message["chat"]["id"], "当前剩余数量:" + str(lentj(bot=bot)), "HTML")
36 |
37 |
38 | def shuaxin(bot,urls):
39 | header = {
40 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36'}
41 | data = {
42 | "email": "",
43 | "passwd": "",
44 | }
45 | # 通过session模拟登录,每次请求带着session
46 | url1 = 'https://dlercloud.com/auth/login'
47 | sess = requests.Session()
48 | f = sess.post(url1, data=data, headers=header)
49 | # print(f.text)
50 | e = sess.get(url=urls, headers=header)
51 | html = e.text
52 | # print(html)
53 | soup = BeautifulSoup(html, 'lxml')
54 | codelist = soup.find_all('a', class_='copy-text')
55 | # print(codelist)
56 | code = []
57 | for i in codelist:
58 | # print(i['data-clipboard-text'])
59 | if i['data-clipboard-text'] == 'https://dlercloud.com/auth/register?affid=56029':
60 | pass
61 | else:
62 | code.append(i['data-clipboard-text'])
63 | if len(code) <= 1:
64 | return 1
65 | else:
66 | n = '\n'
67 | http = 'https://dlercloud.com'
68 | for c in code:
69 | if http + c + n in codelist1:
70 | print(c + "存在")
71 | pass
72 | else:
73 | codelist1.append(http + c + n)
74 | return 0
75 |
76 |
77 | def lentj(bot):
78 | count = 0
79 | for index, line in enumerate(open(bot.plugin_dir + 'invite_code/code.txt', 'r')):
80 | count += 1
81 | return count
--------------------------------------------------------------------------------
/plugins/updatacode/updatacode.py:
--------------------------------------------------------------------------------
1 | import requests
2 | # import re
3 | import json
4 | from bs4 import BeautifulSoup
5 |
6 | global codelist1
7 | codelist1 = []
8 | url = ["https://xn--i2ru8q2qg.com/user/invite", "https://xn--i2ru8q2qg.com/user/invite?page=2"]
9 |
10 | chongfu = 0
11 |
12 |
13 | def updatacode(bot,message):
14 | if str(message["from"]["id"]) == bot.root_id:
15 | for abc in url:
16 | a = shuaxin(bot,abc)
17 | if a == 1:
18 | status = bot.sendChatAction(message["chat"]["id"], "typing")
19 | bot.sendMessage(message["chat"]["id"], "更新失败,cookie失效或是遇到防火墙", "HTML")
20 | else:
21 | status = bot.sendChatAction(message["chat"]["id"], "typing")
22 | bot.sendMessage(message["chat"]["id"], "更新成功", "HTML")
23 | file = open(bot.plugin_dir + 'invite_code/code.txt', 'w+')
24 | for code1 in codelist1:
25 | if code1 not in file:
26 | file.write(str(code1))
27 | else:
28 | print(code1+"存在")
29 | pass
30 | codelist1.clear()
31 | else:
32 | status = bot.sendChatAction(message["chat"]["id"], "typing")
33 | bot.sendMessage(message["chat"]["id"], "您没有权限哦", "HTML")
34 | status = bot.sendChatAction(message["chat"]["id"], "typing")
35 | bot.sendMessage(message["chat"]["id"], "当前剩余数量:" + str(lentj(bot=bot)), "HTML")
36 |
37 |
38 | def shuaxin(bot,urls):
39 | header = {
40 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36'}
41 | data = {
42 | "email": "",
43 | "passwd": "",
44 | }
45 | # 通过session模拟登录,每次请求带着session
46 | url1 = 'https://xn--i2ru8q2qg.com/auth/login'
47 | sess = requests.Session()
48 | f = sess.post(url1, data=data, headers=header)
49 | # print(f.text)
50 | e = sess.get(urls, headers=header)
51 | html = e.text
52 | soup = BeautifulSoup(html, 'lxml')
53 | codelist = soup.find_all('a', target='_blank')
54 | # print(codelist)
55 | code = []
56 | cloudflare = 'https://xn--i2ru8q2qg.comhttps://www.cloudflare.com/5xx-error-landing?utm_source=iuam'
57 | for i in codelist:
58 | code.append(i.get('href'))
59 | if len(code) <= 1:
60 | return 1
61 | else:
62 | n = '\n'
63 | http = 'https://xn--i2ru8q2qg.com'
64 | with open(bot.plugin_dir + 'invite_code/usertext.json') as f1:
65 | userjson = json.load(f1)
66 | for c in code:
67 | if http + c + n in userjson.values() or http + c + n in codelist1:
68 | # print(c + "存在")
69 | pass
70 | else:
71 | codelist1.append(http + c + n)
72 | return 0
73 |
74 |
75 | def lentj(bot):
76 | count = 0
77 | for index, line in enumerate(open(bot.plugin_dir + 'invite_code/code.txt', 'r')):
78 | count += 1
79 | return count
80 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | import sys
2 | sys.path.append("./teelebot")
3 |
4 | from setuptools import setup, find_packages, Extension
5 | from distutils.core import setup, Extension
6 | from version import(
7 | __author__,
8 | __email__,
9 | __blog__,
10 | __description__,
11 | __version__
12 | )
13 |
14 | with open('README.md', "r", encoding="utf-8") as README_md:
15 | README = README_md.read()
16 |
17 | setup(
18 | name='teelebot',
19 | version=__version__,
20 | description=__description__,
21 | keywords=' '.join([
22 | 'teelebot',
23 | 'telegram bot',
24 | 'telegram bot api',
25 | "telegram"
26 | ]),
27 | classifiers=[
28 | "Programming Language :: Python :: 3",
29 | "Operating System :: OS Independent",
30 | "License :: OSI Approved :: GNU General Public License v3 (GPLv3)",
31 | ],
32 | url=__blog__,
33 | author=__author__,
34 | author_email=__email__,
35 | long_description=README,
36 | long_description_content_type="text/markdown",
37 | license='GPLv3',
38 | packages=find_packages(exclude=['plugins', 'plugins.*', 'test', 'test.*']),
39 | package_data={
40 | 'teelebot':['README.md'],
41 | 'teelebot':['LICENSE'],
42 | 'teelebot':[
43 | 'plugins/Chat/hello.ogg',
44 | 'plugins/Hello/helloworld.png',
45 | 'plugins/About/icon.png'
46 | ],
47 | },
48 | python_requires='>=3.5',
49 | install_requires=['requests'],
50 | entry_points={
51 | 'console_scripts': [
52 | 'teelebot=teelebot:main',
53 | ]
54 | },
55 | zip_safe=True
56 | )
--------------------------------------------------------------------------------
/supervisor.conf:
--------------------------------------------------------------------------------
1 | [program:teelebot]
2 | directory=/home/teelebot/
3 | command=python3 -m teelebot -c config.cfg
4 | autostart=true
5 | autorestart=true
6 | startsecs=2
7 | stopsignal=INT
8 | redirect_stderr=true
9 |
--------------------------------------------------------------------------------
/teelebot/__init__.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | """
3 | @creation date: 2019-8-23
4 | @last modify: 2020-11-23
5 | """
6 | import os
7 | import requests
8 | from requests.packages.urllib3.exceptions import InsecureRequestWarning
9 | from .polling import _runUpdates
10 | from .webhook import _runWebhook
11 | from .teelebot import Bot
12 |
13 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
14 |
15 | name = "teelebot"
16 | __all__ = ['Bot']
17 |
18 | bot = Bot()
19 | VERSION = bot.version
20 |
21 | if bot._local_api_server != "False":
22 | api_server = "Local"
23 | else:
24 | api_server = "Remote"
25 |
26 |
27 | def main():
28 | print(" * Self-checking...", end="\r")
29 | req = requests.post(url=bot._url + "getWebhookInfo", verify=False)
30 | if not req.json().get("ok"):
31 | if (req.json().get("error_code") == 401 and
32 | req.json().get("description") == "Unauthorized"):
33 | print("\nif you already logout the bot from the cloud Bot API server,please wait at least 10 minutes and try again.")
34 | else:
35 | print("\nfailed to get running mode!")
36 | os._exit(0)
37 |
38 | status = req.json().get("result")
39 | pending_update_count = status["pending_update_count"]
40 |
41 | if bot._webhook:
42 | protocol = "https://"
43 | if bot._local_api_server != "False":
44 | protocol = "http://"
45 | url = protocol + str(bot._server_address + ":" + str(
46 | bot._server_port) + "/bot" + str(bot._key))
47 | if (bot._drop_pending_updates == True and pending_update_count != 0) \
48 | or (status["url"] != url) or (status["has_custom_certificate"] != bot._self_signed)\
49 | or status["max_connections"] != int(bot._pool_size):
50 | if bot._self_signed:
51 | status = bot.setWebhook(
52 | url=url,
53 | certificate=bot._cert_pub,
54 | max_connections=bot._pool_size,
55 | drop_pending_updates=bot._drop_pending_updates
56 | )
57 | else:
58 | status = bot.setWebhook(
59 | url=url,
60 | max_connections=bot._pool_size,
61 | drop_pending_updates=bot._drop_pending_updates
62 | )
63 | if not status:
64 | print("\nfailed to set Webhook!")
65 | os._exit(0)
66 |
67 | print(" * The teelebot starts running",
68 | "\n * Version : v" + VERSION,
69 | "\n * Mode : Webhook",
70 | "\n * Thread : " + str(bot._pool_size),
71 | "\n * Server : " + api_server + "\n")
72 | _runWebhook(bot=bot,
73 | host=bot._local_address,port=int(bot._local_port))
74 |
75 | else:
76 | if status["url"] != "" or status["has_custom_certificate"]:
77 | status = bot.deleteWebhook()
78 | if not status:
79 | print("\nfailed to set getUpdates!")
80 | os._exit(0)
81 |
82 | print(" * The teelebot starts running",
83 | "\n * Version : v" + VERSION,
84 | "\n * Mode : Polling",
85 | "\n * Thread : " + str(bot._pool_size),
86 | "\n * Server : " + api_server + "\n")
87 | if bot._drop_pending_updates == True and \
88 | pending_update_count != 0:
89 | results = bot.getUpdates()
90 | messages = bot._washUpdates(results)
91 | _runUpdates(bot=bot)
92 |
--------------------------------------------------------------------------------
/teelebot/__main__.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | '''
3 | @creation date: 2019-8-23
4 | @last modify: 2020-6-25
5 | '''
6 |
7 | if __name__ == "__main__":
8 | import teelebot
9 | teelebot.main()
--------------------------------------------------------------------------------
/teelebot/handler.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | '''
3 | @creation date: 2019-8-23
4 | @last modify: 2020-11-23
5 | '''
6 | import configparser
7 | import argparse
8 | import os
9 | import sys
10 | import shutil
11 | import requests
12 | from requests.packages.urllib3.exceptions import InsecureRequestWarning
13 | from pathlib import Path
14 | from .version import __author__, __github__, __version__
15 |
16 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
17 |
18 | cloud_api_server = "https://api.telegram.org/"
19 |
20 | parser = argparse.ArgumentParser(description="teelebot console command list")
21 | parser.add_argument("-c", "--config", type=str,
22 | help="specify the configuration file")
23 | parser.add_argument("-k", "--key", type=str,
24 | help="Specify the bot key")
25 | parser.add_argument("-r", "--root", type=str,
26 | help="Specify the root user id")
27 | parser.add_argument("-p", "--plugin", type=str,
28 | help="create a plugin template")
29 | parser.add_argument("-L", "--logout",
30 | help="use it to log out from the cloud Bot API server before launching the bot locally.",
31 | action="store_true")
32 | parser.add_argument("-C", "--close",
33 | help="use it to close the bot instance before moving it from one local server to another.",
34 | action="store_true")
35 | parser.add_argument(
36 | "-d", "--debug", help="run teelebot in debug mode", action="store_true")
37 | parser.add_argument(
38 | "-v", "--version", help="view the current version of teelebot", action="store_true")
39 | args = parser.parse_args()
40 |
41 | if len(sys.argv) == 2 and args.version:
42 | print("\nVersion: " + __version__)
43 | print("Author: " + __author__)
44 | print("Project: " + __github__)
45 | os._exit(0)
46 |
47 |
48 | def _config():
49 | '''
50 | 获取bot配置信息及初始化
51 | '''
52 | config = {}
53 |
54 | if len(sys.argv) == 3 and args.config:
55 | config_dir = os.path.abspath(str(Path(args.config)))
56 | else:
57 | config_dir = str(Path(os.path.abspath(
58 | os.path.expanduser('~')) + "/.teelebot/config.cfg"))
59 | (path, filename) = os.path.split(config_dir)
60 | (filename_, extension) = os.path.splitext(filename)
61 | if extension != ".cfg":
62 | print("only support configuration files with .cfg suffix.")
63 | os._exit(0)
64 | if not os.path.exists(str(Path(path))):
65 | os.makedirs(str(Path(path)))
66 | if not os.path.exists(str(Path(config_dir))):
67 | print("the configuration file does not exist.")
68 | key = ""
69 | if args.key:
70 | key = args.key
71 | root = ""
72 | if args.root:
73 | root = args.root
74 | with open(config_dir, "w") as conf_file:
75 | conf_file.writelines([
76 | "[config]" + "\n",
77 | "key=" + str(key) + "\n",
78 | "root_id=" + str(root) + "\n",
79 | "plugin_dir=" + "\n",
80 | "pool_size=40" + "\n",
81 | "debug=False" + "\n",
82 | "local_api_server=False" + "\n",
83 | "drop_pending_updates=False" + "\n",
84 | "webhook=False" + "\n",
85 | "self_signed=False" + "\n",
86 | "cert_key=" + "\n",
87 | "cert_pub=" + "\n",
88 | "server_address=" + "\n",
89 | "server_port=" + "\n",
90 | "local_address=" + "\n",
91 | "local_port="
92 | ])
93 | print("the configuration file has been created automatically.")
94 | print("configuration file path: " + str(config_dir))
95 | if not args.key or not args.root:
96 | print("please modify the relevant parameters and restart the teelebot.")
97 | os._exit(0)
98 | # else:
99 | # print("\n")
100 |
101 | conf = configparser.ConfigParser()
102 | conf.read(config_dir)
103 | options = conf.options("config")
104 |
105 | if args.debug:
106 | conf.set("config", "debug", str(True))
107 | if args.key:
108 | conf.set("config", "key", str(args.key))
109 | if args.root:
110 | conf.set("config", "root_id", str(args.root))
111 |
112 | if args.debug:
113 | default_args = ["key", "webhook", "root_id", "debug"]
114 | else:
115 | default_args = ["key", "webhook", "root_id"]
116 | for default_arg in default_args:
117 | if default_arg not in options:
118 | print("the configuration file is missing necessary parameters.",
119 | "\nnecessary parameters:" + default_args)
120 | os._exit(0)
121 |
122 | for option in options:
123 | config[str(option)] = conf.get("config", option)
124 |
125 | none_count = 0
126 | for default_arg in default_args:
127 | if config[default_arg] == "" or\
128 | config[default_arg] == None:
129 | none_count += 1
130 | print("field " + default_arg + " is not set in configuration file.")
131 | if none_count != 0:
132 | os._exit(0)
133 |
134 | if any(["version" in config.keys(), "author" in config.keys()]):
135 | print("error in configuration file.")
136 | os._exit(0)
137 |
138 | if config["webhook"] == "True":
139 | webhook_args = ["self_signed",
140 | "server_address", "server_port",
141 | "local_address", "local_port",
142 | "cert_pub", "cert_key"]
143 | for w in webhook_args:
144 | if w not in config.keys():
145 | print("please check if the following fields exist in the configuration file: \n" +
146 | "cert_pub cert_key self_signed server_address server_port local_address local_port")
147 | os._exit(0)
148 |
149 | plugin_dir_in_config = False
150 | if "plugin_dir" in config.keys():
151 | if config["plugin_dir"] == "" or config["plugin_dir"] == None:
152 | plugin_dir = str(Path(os.path.dirname(os.path.abspath(__file__)) + r"/plugins/")) + os.sep
153 | else:
154 | plugin_dir = str(Path(os.path.abspath(config["plugin_dir"]))) + os.sep
155 | plugin_dir_in_config = True
156 | else:
157 | plugin_dir = str(Path(os.path.dirname(os.path.abspath(__file__)) + r"/plugins/")) + os.sep
158 |
159 | if os.path.exists(str(Path(os.path.dirname(
160 | os.path.abspath(__file__)) + r"/__pycache__"))):
161 | shutil.rmtree(str(Path(os.path.dirname(
162 | os.path.abspath(__file__)) + r"/__pycache__")))
163 |
164 | if not os.path.isdir(plugin_dir): # 插件目录检测
165 | # os.makedirs(plugin_dir)
166 | os.mkdir(plugin_dir)
167 | with open(str(Path(plugin_dir + "__init__.py")), "w") as f:
168 | pass
169 | elif not os.path.exists(str(Path(plugin_dir + "__init__.py"))):
170 | with open(str(Path(plugin_dir + "__init__.py")), "w") as f:
171 | pass
172 |
173 | if args.plugin and plugin_dir_in_config: #插件模板创建
174 | plugin_name = args.plugin
175 | if not os.path.exists(str(Path(plugin_dir + plugin_name))):
176 | os.mkdir(str(Path(plugin_dir + plugin_name)))
177 | if not os.path.exists(str(Path(plugin_dir + plugin_name + os.sep + plugin_name + ".py"))):
178 | with open(str(Path(plugin_dir + plugin_name + os.sep + plugin_name + ".py")), "w") as enter:
179 | enter.writelines([
180 | "# -*- coding:utf-8 -*-\n",
181 | "\n",
182 | "def " + plugin_name + "(bot, message):\n",
183 | "\n" + \
184 | " # root_id = bot.root_id\n" + \
185 | " # bot_id = bot.bot_id\n" + \
186 | " # author = bot.author\n" + \
187 | " # version = bot.version\n" + \
188 | " # plugin_dir = bot.plugin_dir\n" + \
189 | " # plugin_bridge = bot.plugin_bridge\n" + \
190 | " # uptime = bot.uptime\n" + \
191 | " # response_times = bot.response_times\n" + \
192 | " # response_chats = bot.response_chats\n" + \
193 | " # response_users = bot.response_users\n" + \
194 | "\n" + \
195 | ' chat_id = message["chat"]["id"]\n' + \
196 | ' user_id = message["from"]["id"]\n' + \
197 | ' message_id = message["message_id"]\n' + \
198 | "\n" + \
199 | ' message_type = message["message_type"]\n' + \
200 | ' chat_type = message["chat"]["type"]\n' + \
201 | "\n" + \
202 | ' prefix = "/' + plugin_name.lower() + '"\n' + \
203 | "\n\n" + \
204 | " # Write your plugin code below"
205 | ])
206 | if not os.path.exists(str(Path(plugin_dir + plugin_name + os.sep + "__init__.py"))):
207 | with open(str(Path(plugin_dir + plugin_name + os.sep + "__init__.py")), "w") as init:
208 | init.writelines([
209 | "#/" + plugin_name.lower() + "\n",
210 | "#" + plugin_name + " Plugin\n"
211 | ])
212 | if not os.path.exists(str(Path(plugin_dir + plugin_name + os.sep + "readme.md"))):
213 | with open(str(Path(plugin_dir + plugin_name + os.sep + "readme.md")), "w") as readme:
214 | readme.writelines([
215 | "# " + plugin_name + " #\n"
216 | ])
217 | if not os.path.exists(str(Path(plugin_dir + plugin_name + os.sep + "requirement.txt"))):
218 | with open(str(Path(plugin_dir + plugin_name + os.sep + "requirement.txt")), "w") as requirement:
219 | pass
220 |
221 | print("plugin " + plugin_name + " was created successfully.")
222 | else:
223 | print("plugin " + plugin_name + " already exists.")
224 | os._exit(0)
225 | elif args.plugin and not plugin_dir_in_config:
226 | print("the plugin_dir is not set in the configuration file.")
227 | os._exit(0)
228 |
229 | if "pool_size" in config.keys():
230 | if int(config["pool_size"]) < 1 or int(config["pool_size"]) > 100:
231 | print("thread pool size is out of range (1-100).")
232 | os._exit(0)
233 | else:
234 | config["pool_size"] = "40"
235 |
236 | if "local_api_server" in config.keys():
237 | local_api_server = config["local_api_server"]
238 | if (local_api_server == None or
239 | local_api_server == "" or
240 | local_api_server == "False" or
241 | len(local_api_server) < 7):
242 | config["local_api_server"] = "False"
243 | else:
244 | if "https://" in local_api_server:
245 | print("local api server address not support https.")
246 | os._exit(0)
247 | if "http://" not in local_api_server:
248 | print("local api server address incorrect.")
249 | os._exit(0)
250 | if "telegram.org" in local_api_server:
251 | print("local api server address incorrect.")
252 | os._exit(0)
253 | if local_api_server[len(local_api_server)-1] != "/":
254 | local_api_server += "/"
255 | config["local_api_server"] = local_api_server
256 | else:
257 | config["local_api_server"] = "False"
258 |
259 | if "self_signed" in config.keys():
260 | if config["self_signed"] == "True":
261 | config["self_signed"] = True
262 | elif config["self_signed"] == "False":
263 | config["self_signed"] = False
264 | else:
265 | print("The self_signed field value in the configuration file is wrong.")
266 | os._exit(0)
267 | else:
268 | config["self_signed"] = False
269 |
270 | if "drop_pending_updates" in config.keys():
271 | if config["drop_pending_updates"] == "True":
272 | config["drop_pending_updates"] = True
273 | elif config["drop_pending_updates"] == "False":
274 | config["drop_pending_updates"] = False
275 | else:
276 | print("The drop_pending_updates field value in the configuration file is wrong.")
277 | os._exit(0)
278 | else:
279 | config["drop_pending_updates"] = False
280 |
281 | if config["debug"] == "True":
282 | config["debug"] = True
283 | elif config["debug"] == "False":
284 | config["debug"] = False
285 | else:
286 | print("The debug field value in the configuration file is wrong.")
287 | os._exit(0)
288 |
289 | if config["webhook"] == "True":
290 | config["webhook"] = True
291 | elif config["webhook"] == "False":
292 | config["webhook"] = False
293 | else:
294 | print("The webhook field value in the configuration file is wrong.")
295 | os._exit(0)
296 |
297 | config["author"] = __author__
298 | config["version"] = __version__
299 | config["plugin_dir"] = plugin_dir
300 | config["plugin_bridge"] = _bridge(config["plugin_dir"])
301 | config["plugin_info"] = _plugin_info(
302 | config["plugin_bridge"].keys(), config["plugin_dir"])
303 | config["cloud_api_server"] = cloud_api_server
304 |
305 | if args.debug:
306 | config["debug"] = True
307 |
308 | # print(config)
309 | return config
310 |
311 | def _bridge(plugin_dir):
312 | '''
313 | 获取插件和指令的映射
314 | '''
315 | plugin_bridge = {}
316 | plugin_list = []
317 |
318 | plugin_lis = os.listdir(plugin_dir)
319 | for plugi in plugin_lis:
320 | if os.path.isdir(str(Path(plugin_dir + plugi))) and plugi != "__pycache__" and plugi[0] != '.':
321 | plugin_list.append(plugi)
322 | for plugin in plugin_list:
323 | with open(str(Path(plugin_dir + plugin + r"/__init__.py")), encoding="utf-8") as f:
324 | row_one = f.readline().strip()[1:]
325 | if row_one != "~~": # Hidden plugin
326 | plugin_bridge[plugin] = row_one
327 |
328 | # print(plugin_bridge)
329 | return plugin_bridge
330 |
331 | def _plugin_info(plugin_list, plugin_dir):
332 | '''
333 | 获取插件修改状态
334 | '''
335 | plugin_info = {}
336 | for plugin in plugin_list:
337 | mtime = os.stat(str(Path(plugin_dir + plugin + "/" + plugin + ".py"))).st_mtime
338 | plugin_info[plugin] = mtime
339 |
340 | return plugin_info
341 |
342 |
343 | if args.close and args.logout:
344 | print("only one of logout and close can be used at the same time.")
345 | os._exit(0)
346 |
347 | elif args.logout and not args.close:
348 | config = _config()
349 | logout_url = cloud_api_server + "bot" + config["key"] + "/logOut"
350 | try:
351 | req = requests.post(url=logout_url, verify=False)
352 | except:
353 | print("error request the cloud Bot API server.")
354 | os._exit(0)
355 | if req.json().get("ok"):
356 | print("successfully log out from the cloud Bot API server.")
357 | elif not req.json().get("ok"):
358 | print("error log out from the cloud Bot API server.")
359 | if (req.json().get("error_code") == 401 and
360 | req.json().get("description") == "Unauthorized"):
361 | print("if you already logout the bot from the cloud Bot API server,please wait at least 10 minutes and try again.")
362 | os._exit(0)
363 |
364 | elif args.close and not args.logout:
365 | config = _config()
366 | if config["local_api_server"] == "False":
367 | print("close can only be used when local_api_server is configured.")
368 | os._exit(0)
369 |
370 | close_url = config["local_api_server"] + "bot" + config["key"] + "/close"
371 | try:
372 | req = requests.post(url=close_url, verify=False)
373 | except:
374 | print("error request the the local API server.")
375 | os._exit(0)
376 | if req.json().get("ok"):
377 | print("successfully close from the local API server.")
378 | elif not req.json().get("ok"):
379 | print("error close from the local API server.")
380 | if req.json().get("error_code") == 429:
381 | print("too many requests, please retry after " + str(req.json().get("parameters")["retry_after"]) + " seconds.")
382 | os._exit(0)
383 |
384 |
385 |
--------------------------------------------------------------------------------
/teelebot/logger.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | '''
3 | @creation date: 2019-11-15
4 | @last modify: 2020-11-18
5 | '''
6 | import logging as __logging
7 |
8 | __logging.basicConfig(level=__logging.DEBUG,
9 | datefmt='%Y/%m/%d %H:%M:%S',
10 | format='%(asctime)s - %(levelname)s - %(message)s')
11 | __logging.getLogger("urllib3").setLevel(__logging.WARNING)
12 |
13 | _logger = __logging.getLogger(__name__)
--------------------------------------------------------------------------------
/teelebot/plugins/About/About.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | import os
3 |
4 | def About(bot, message):
5 | chat_id = message["chat"]["id"]
6 | message_id = message["message_id"]
7 | text = message["text"]
8 | bot_id = bot.bot_id
9 | prefix = "about"
10 |
11 | plugin_dir = bot.plugin_dir
12 | VERSION = bot.version
13 |
14 | if not os.path.exists(bot.path_converter(plugin_dir + "About/config.ini")):
15 | first_btn = ["交流群组", "https://t.me/teelebot_chat"]
16 | last_btn = ["项目地址", "https://github.com/plutobell/teelebot"]
17 | else:
18 | with open(bot.path_converter(plugin_dir + "About/config.ini"), 'r') as g:
19 | first_btn = g.readline().strip().split(',')
20 | last_btn = g.readline().strip().split(',')
21 |
22 | if text[1:len(prefix)+1] == prefix:
23 | inlineKeyboard = [
24 | [
25 | {"text": first_btn[0], "url": first_btn[1]},
26 | {"text": last_btn[0], "url": last_btn[1]},
27 | ]
28 | ]
29 | reply_markup = {
30 | "inline_keyboard": inlineKeyboard
31 | }
32 | status = bot.sendChatAction(chat_id, "typing")
33 | msg = "此 Bot 基于 teelebot 框架 v" + VERSION + "\n\n" +\
34 | "teelebot 是基于 Telegram Bot API 的 Bot 框架,具有插件系统,扩展方便。\n\n"
35 |
36 | req = bot.getUserProfilePhotos(user_id=str(bot_id), limit=1)
37 | if req.get("photos", "notphotos") != "notphotos":
38 | bot_icon = req.get("photos")[0][0]["file_id"]
39 | if type(bot_icon) == str and len(bot_icon) > 50:
40 | photo = bot_icon
41 | else:
42 | with open(bot.path_converter(plugin_dir + "About/icon.png"), "rb") as p:
43 | photo = p.read()
44 | else:
45 | with open(bot.path_converter(plugin_dir + "About/icon.png"), "rb") as p:
46 | photo = p.read()
47 |
48 | status = bot.sendPhoto(chat_id=chat_id, photo=photo, caption=msg, parse_mode="HTML", reply_to_message_id=message_id, reply_markup=reply_markup)
49 | bot.message_deletor(15, chat_id, status["message_id"])
50 | else:
51 | status = bot.sendChatAction(chat_id, "typing")
52 | status = bot.sendMessage(chat_id=chat_id, text="指令格式错误,请检查!", parse_mode="HTML", reply_to_message_id=message_id)
53 | bot.message_deletor(15, chat_id, status["message_id"])
54 |
55 |
--------------------------------------------------------------------------------
/teelebot/plugins/About/__init__.py:
--------------------------------------------------------------------------------
1 | #/about
2 | #About 关于
--------------------------------------------------------------------------------
/teelebot/plugins/About/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Everless321/telebot-invitecode/9ebbf77e85620485574b7bebd61d247e9a1d6612/teelebot/plugins/About/icon.png
--------------------------------------------------------------------------------
/teelebot/plugins/Chat/Chat.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | import requests
3 | import urllib.parse as ubp
4 | requests.adapters.DEFAULT_RETRIES = 5
5 |
6 | def Chat(bot, message):
7 | plugin_dir = bot.plugin_dir
8 |
9 | url = "http://api.qingyunke.com/api.php?key=free&appid=0&msg="
10 | hello = ("你好", "nihao", "hello", "Hello",
11 | "HELLO", "hi", "Hi", "HI",
12 | "早上好", "上午好", "下午好", "晚上好", "中午好",
13 | "good morning", "Good morning", "good afternoom",
14 | "Good afternoom", "good evening", "Good evening")
15 | if message["text"][1:] in hello:
16 | status = bot.sendChatAction(message["chat"]["id"], "typing")
17 | status = bot.sendVoice(message["chat"]["id"], voice=bot.path_converter(plugin_dir + "Chat/hello.ogg"),
18 | reply_to_message_id=message["message_id"])
19 | else:
20 | try:
21 | with requests.post(url + ubp.quote(message["text"][1:])) as req: #urlencode编码
22 | req.keep_alive = False
23 | req.encoding = "utf-8"
24 | if not req.status_code == requests.codes.ok:
25 | status = bot.sendChatAction(message["chat"]["id"], "typing")
26 | status = bot.sendMessage(chat_id=message["chat"]["id"], text="接口调用失败!",
27 | parse_mode="HTML", reply_to_message_id=message["message_id"])
28 | bot.message_deletor(15, status["chat"]["id"], status["message_id"])
29 | else:
30 | try:
31 | msg = str(req.json().get("content").replace("{br}", "\n").replace("菲菲", "小埋"))
32 | if "{face:" in msg:
33 | msg = msg.split("}")[1]
34 | except:
35 | msg = "出错了."
36 | status = bot.sendChatAction(message["chat"]["id"], "typing")
37 | status = bot.sendMessage(message["chat"]["id"],text=msg,
38 | parse_mode="HTML", reply_to_message_id=message["message_id"])
39 | except Exception as e:
40 | print(e)
41 |
42 |
43 | def timer_func(bot, chat_id, message_id):
44 | status = bot.deleteMessage(chat_id=chat_id, message_id=message_id)
--------------------------------------------------------------------------------
/teelebot/plugins/Chat/__init__.py:
--------------------------------------------------------------------------------
1 | #@
2 | #Chat插件 人工智障在线陪聊
--------------------------------------------------------------------------------
/teelebot/plugins/Chat/hello.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Everless321/telebot-invitecode/9ebbf77e85620485574b7bebd61d247e9a1d6612/teelebot/plugins/Chat/hello.ogg
--------------------------------------------------------------------------------
/teelebot/plugins/Hello/Hello.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 |
3 | def Hello(bot, message):
4 | #print("你好,世界!")
5 | bot.sendChatAction(message["chat"]["id"], "typing")
6 | bot.sendPhoto(message["chat"]["id"], bot.path_converter(bot.plugin_dir + "Hello/helloworld.png"), reply_to_message_id=message["message_id"])
--------------------------------------------------------------------------------
/teelebot/plugins/Hello/__init__.py:
--------------------------------------------------------------------------------
1 | #/helloworld
2 | #Hello World插件例子
--------------------------------------------------------------------------------
/teelebot/plugins/Hello/helloworld.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Everless321/telebot-invitecode/9ebbf77e85620485574b7bebd61d247e9a1d6612/teelebot/plugins/Hello/helloworld.png
--------------------------------------------------------------------------------
/teelebot/plugins/Menu/Menu.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | '''
3 | creation time: 2019-8-15
4 | last_modify: 2020-11-16
5 | '''
6 | import os
7 |
8 | def Menu(bot, message):
9 | chat_id = message["chat"]["id"]
10 | message_id = message["message_id"]
11 | chat_type = message["chat"]["type"]
12 |
13 | prefix = "start"
14 |
15 | plugin_bridge = bot.plugin_bridge
16 | plugin_dir = bot.plugin_dir
17 | plugin_list = list(plugin_bridge.keys())
18 |
19 | if chat_type != "private" and "/pluginctl" in plugin_bridge.values() and plugin_bridge["PluginCTL"] == "/pluginctl":
20 | if os.path.exists(bot.path_converter(plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db")):
21 | with open(bot.path_converter(plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db"), "r") as f:
22 | plugin_setting = f.read().strip()
23 | plugin_list_off = plugin_setting.split(',')
24 | plugin_list_temp = []
25 | for plugin in plugin_bridge.keys():
26 | if plugin not in plugin_list_off:
27 | plugin_list_temp.append(plugin)
28 | plugin_list = plugin_list_temp
29 |
30 | plugin_count = len(plugin_list)
31 | page_size = 5
32 | page_total = int((plugin_count + page_size - 1) / page_size) # 总页数=(总数+每页数量-1)/每页数量
33 | page_callback_command = "/" + prefix + "page?page="
34 |
35 | if not os.path.exists(bot.path_converter(plugin_dir + "Menu/config.ini")):
36 | first_btn = ["交流群组", "https://t.me/teelebot_chat"]
37 | last_btn = ["项目地址", "https://github.com/plutobell/teelebot"]
38 | else:
39 | with open(bot.path_converter(plugin_dir + "Menu/config.ini"), 'r') as g:
40 | first_btn = g.readline().strip().split(',')
41 | last_btn = g.readline().strip().split(',')
42 |
43 | wait_time = plugin_count * 7
44 |
45 | if "reply_markup" in message.keys():
46 | click_user_id = message["click_user"]["id"]
47 | from_user_id = message["reply_to_message"]["from"]["id"]
48 | callback_query_data = message["callback_query_data"]
49 |
50 | if callback_query_data[:len(page_callback_command)] == page_callback_command:
51 | if click_user_id == from_user_id:
52 | page = int(callback_query_data.split('=')[1])
53 | page, menu_str = menu_text(bot, plugin_dir=plugin_dir, page=page, page_total=page_total, page_size=page_size, plugin_list=plugin_list)
54 | previous_page = page - 1
55 | if previous_page < 1:
56 | previous_page = 1
57 | next_page = page + 1
58 | if next_page > page_total:
59 | next_page = page_total
60 |
61 | if page_total == 1:
62 | inlineKeyboard = [
63 | [
64 | {"text": first_btn[0], "url": first_btn[1]},
65 | {"text": last_btn[0], "url": last_btn[1]},
66 | ]
67 | ]
68 | elif page == 1:
69 | inlineKeyboard = [
70 | [
71 | {"text": first_btn[0], "url": first_btn[1]},
72 | {"text": "下一页", "callback_data": page_callback_command + str(page+1)},
73 | ]
74 | ]
75 | elif page == page_total:
76 | inlineKeyboard = [
77 | [
78 | {"text": "上一页", "callback_data": page_callback_command + str(page-1)},
79 | {"text": last_btn[0], "url": last_btn[1]},
80 | ]
81 | ]
82 | else:
83 | inlineKeyboard = [
84 | [
85 | {"text": "上一页", "callback_data": page_callback_command + str(previous_page)},
86 | {"text": "下一页", "callback_data": page_callback_command + str(next_page)},
87 | ]
88 | ]
89 | reply_markup = {
90 | "inline_keyboard": inlineKeyboard
91 | }
92 | status = bot.editMessageText(chat_id=chat_id, message_id=message_id, text=menu_str, parse_mode="HTML", reply_markup=reply_markup)
93 | status = bot.answerCallbackQuery(message["callback_query_id"])
94 | else:
95 | status = bot.answerCallbackQuery(message["callback_query_id"], text="点啥点,关你啥事?", show_alert=bool("true"))
96 | else:
97 | page = 1
98 | if page_total == 1:
99 | inlineKeyboard = [
100 | [
101 | {"text": first_btn[0], "url": first_btn[1]},
102 | {"text": last_btn[0], "url": last_btn[1]},
103 | ]
104 | ]
105 | else:
106 | inlineKeyboard = [
107 | [
108 | {"text": first_btn[0], "url": first_btn[1]},
109 | {"text": "下一页", "callback_data": page_callback_command + str(page+1)},
110 | ]
111 | ]
112 | reply_markup = {
113 | "inline_keyboard": inlineKeyboard
114 | }
115 |
116 | page, menu_str = menu_text(bot=bot, plugin_dir=plugin_dir, page=page, page_total=page_total, page_size=page_size, plugin_list=plugin_list)
117 |
118 | status = bot.sendChatAction(chat_id, "typing")
119 | status = bot.sendMessage(chat_id=chat_id, text=menu_str, parse_mode="HTML", reply_to_message_id=message_id, reply_markup=reply_markup)
120 |
121 | bot.message_deletor(wait_time, message["chat"]["id"], status["message_id"])
122 |
123 | def menu_text(bot, plugin_dir, page, page_total, page_size, plugin_list):
124 | VERSION = bot.version
125 | if page < 1:
126 | page = 1
127 | elif page > page_total:
128 | page = page_total
129 |
130 | if page >=1 and page <= page_total:
131 | menu_str = ""
132 | plugin_range = range(page*page_size-page_size, page*page_size-1+1)
133 | for i, plugin in enumerate(plugin_list): #(now_page*page_size-page_size,now_page*page_size-1)
134 | if i in plugin_range:
135 | with open(bot.path_converter(plugin_dir + plugin + r"/__init__.py"), encoding="utf-8") as f:
136 | line_1 = ""
137 | line_2 = ""
138 | for i in range(2):
139 | if i == 0:
140 | line_1 = f.readline().strip()[1:]
141 | elif i == 1:
142 | line_2 = f.readline().strip()[1:]
143 | menu_str += "" + line_1 + " - " + line_2 + "\n\n"
144 | menu_str = "插件列表 [" + str(page) + "/" + str(page_total) + "]\n\n" + menu_str + "\nv" + VERSION + ""
145 |
146 | return page, menu_str
147 |
--------------------------------------------------------------------------------
/teelebot/plugins/Menu/__init__.py:
--------------------------------------------------------------------------------
1 | #/start
2 | #Robot 插件列表
--------------------------------------------------------------------------------
/teelebot/plugins/PluginCTL/PluginCTL.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | '''
3 | creation time: 2020-6-15
4 | last_modify: 2020-11-28
5 | '''
6 | import os
7 | from threading import Lock
8 |
9 |
10 | lock = Lock()
11 |
12 | def PluginCTL(bot, message):
13 | message_id = message["message_id"]
14 | chat_id = message["chat"]["id"]
15 | user_id = message["from"]["id"]
16 | text = message["text"]
17 | prefix = "pluginctl"
18 |
19 | plugin_dir = bot.plugin_dir
20 | root_id = bot.root_id
21 | plugin_bridge = bot.plugin_bridge
22 |
23 | if not os.path.exists(bot.path_converter(plugin_dir + "PluginCTL/db/")):
24 | os.mkdir(bot.path_converter(plugin_dir + "PluginCTL/db/"))
25 |
26 | if message["chat"]["type"] != "private" and not os.path.exists(bot.path_converter(plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db")):
27 | with open(bot.path_converter(plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db"), "w") as f:
28 | pass
29 |
30 | command = {
31 | "/pluginctlshow": "show",
32 | "/pluginctlon": "on",
33 | "/pluginctloff": "off"
34 | }
35 | count = 0
36 | for c in command.keys():
37 | if c in str(text):
38 | count += 1
39 |
40 | if message["chat"]["type"] != "private":
41 | admins = administrators(bot=bot, chat_id=chat_id)
42 | if str(root_id) not in admins:
43 | admins.append(str(root_id)) #root permission
44 |
45 | if message["chat"]["type"] == "private" and text[1:len(prefix)+1] == prefix: #判断是否为私人对话
46 | status = bot.sendChatAction(chat_id, "typing")
47 | status = bot.sendMessage(chat_id, "抱歉,该指令不支持私人会话.", parse_mode="text", reply_to_message_id=message_id)
48 | bot.message_deletor(15, chat_id, status["message_id"])
49 | elif text[1:len(prefix)+1] == prefix and count == 0:
50 | status = bot.sendChatAction(chat_id, "typing")
51 | msg = "PluginCTL 插件功能\n\n" +\
52 | "/pluginctlshow - 展示插件开启状态 \n" +\
53 | "/pluginctlon - 启用插件。格式:/pluginctlon接要启用的插件名,以空格分隔 \n" +\
54 | "/pluginctloff - 禁用插件。格式:/pluginctloff接要禁用的插件名,以空格分隔 \n" +\
55 | "/pluginctlon all - 启用所有插件 \n" +\
56 | "/pluginctloff all - 禁用所有插件,但必须的插件将被保留 \n" +\
57 | "\n同时操作多个插件请用英文逗号分隔\n"
58 | status = bot.sendMessage(chat_id=chat_id, text=msg, parse_mode="HTML", reply_to_message_id=message["message_id"])
59 | bot.message_deletor(30, chat_id, status["message_id"])
60 | elif "reply_markup" in message.keys():
61 | click_user_id = message["click_user"]["id"]
62 | from_user_id = message["reply_to_message"]["from"]["id"]
63 | callback_query_data = message["callback_query_data"]
64 |
65 | pluginctlsho_on_page = "/" + prefix + "showonpage"
66 | pluginctlsho_off_page = "/" + prefix + "showoffpage"
67 |
68 | plugin_dict = bot.plugin_bridge
69 | with open(bot.path_converter(plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db"), "r") as f:
70 | plugin_setting = f.read().strip()
71 | plugin_list_off = plugin_setting.split(',')
72 | plugin_list_on = {}
73 | for plugin in plugin_bridge.keys():
74 | if plugin not in plugin_list_off:
75 | plugin_list_on[plugin] = plugin_bridge[plugin]
76 | for key, val in plugin_list_on.items(): #dict.keys()不可修改!
77 | if val == "" or val == " ":
78 | plugin_list_on[key] = "nil"
79 |
80 | if callback_query_data == pluginctlsho_on_page:
81 | inlineKeyboard = [
82 | [
83 | {"text": "禁用的", "callback_data": "/pluginctlshowoffpage"},
84 | ]
85 | ]
86 | reply_markup = {
87 | "inline_keyboard": inlineKeyboard
88 | }
89 |
90 | if click_user_id == from_user_id:
91 | for key, val in plugin_list_on.items(): #dict.keys()不可修改!
92 | if val == "" or val == " ":
93 | plugin_list_on[key] = "nil"
94 | msg_on = "启用的插件 \n\n"
95 | for i, on in enumerate(plugin_list_on):
96 | msg_on += " [" + str(i+1) + "] " + str(on) + " " + str(plugin_list_on[on]) + "\n"
97 | msg_on += "\nnil 代表指令为空"
98 | status = bot.editMessageText(chat_id=chat_id, message_id=message_id, text=msg_on + "\n", parse_mode="HTML", reply_markup=reply_markup)
99 | status = bot.answerCallbackQuery(message["callback_query_id"])
100 | else:
101 | status = bot.answerCallbackQuery(message["callback_query_id"], text="点啥点,关你啥事?", show_alert=bool("true"))
102 | elif callback_query_data == pluginctlsho_off_page:
103 | inlineKeyboard = [
104 | [
105 | {"text": "启用的", "callback_data": "/pluginctlshowonpage"},
106 | ]
107 | ]
108 | reply_markup = {
109 | "inline_keyboard": inlineKeyboard
110 | }
111 |
112 | if click_user_id == from_user_id:
113 | msg_off = "禁用的插件 \n\n"
114 | for key, val in plugin_bridge.items(): #dict.keys()不可修改!
115 | if val == "" or val == " ":
116 | plugin_bridge[key] = "nil"
117 | for i, pluo in enumerate(plugin_list_off):
118 | if pluo == "" or pluo == " ":
119 | del plugin_list_off[i]
120 | if len(plugin_list_off) == 0:
121 | msg_off += "无\n"
122 | else:
123 | for i, off in enumerate(plugin_list_off):
124 | msg_off += " [" + str(i+1) + "] " + str(off) + " " + str(plugin_bridge[off]) + "\n"
125 | msg_off += "\nnil 代表指令为空"
126 | status = bot.editMessageText(chat_id=chat_id, message_id=message_id, text=msg_off + "\n", parse_mode="HTML", reply_markup=reply_markup)
127 | status = bot.answerCallbackQuery(message["callback_query_id"])
128 | else:
129 | status = bot.answerCallbackQuery(message["callback_query_id"], text="点啥点,关你啥事?", show_alert=bool("true"))
130 |
131 | elif count > 0:
132 | if str(user_id) not in admins:
133 | status = bot.sendChatAction(chat_id, "typing")
134 | status = bot.sendMessage(chat_id=chat_id, text="抱歉,您无权操作.", parse_mode="HTML", reply_to_message_id=message_id)
135 | bot.message_deletor(15, chat_id, status["message_id"])
136 | elif text[1:len(prefix + command["/pluginctlshow"])+1] == prefix + command["/pluginctlshow"]:
137 | inlineKeyboard = [
138 | [
139 | {"text": "禁用的", "callback_data": "/pluginctlshowoffpage"},
140 | ]
141 | ]
142 | reply_markup = {
143 | "inline_keyboard": inlineKeyboard
144 | }
145 |
146 | plugin_dict = bot.plugin_bridge
147 | with open(bot.path_converter(plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db"), "r") as f:
148 | plugin_setting = f.read().strip()
149 | plugin_list_off = plugin_setting.split(',')
150 | plugin_list_on = {}
151 | for plugin in plugin_bridge.keys():
152 | if plugin not in plugin_list_off:
153 | plugin_list_on[plugin] = plugin_bridge[plugin]
154 | for key, val in plugin_list_on.items(): #dict.keys()不可修改!
155 | if val == "" or val == " ":
156 | plugin_list_on[key] = "nil"
157 | msg_on = "启用的插件 \n\n"
158 | for i, on in enumerate(plugin_list_on):
159 | msg_on += " [" + str(i+1) + "] " + str(on) + " " + str(plugin_list_on[on]) + "\n"
160 | msg_on += "\nnil 代表指令为空"
161 | status = bot.sendChatAction(chat_id, "typing")
162 | status = bot.sendMessage(chat_id=chat_id, text=msg_on + "\n", parse_mode="HTML", reply_to_message_id=message_id, reply_markup=reply_markup)
163 | bot.message_deletor(60, chat_id, status["message_id"])
164 | elif text[1:len(prefix + command["/pluginctlon"])+1] == prefix + command["/pluginctlon"]:
165 | plugin_list = list(plugin_bridge.keys())
166 | if len(text.split(' ')) == 2:
167 | plug_set = text.split(' ')[1]
168 | for p in plug_set.split(','):
169 | if p == "nil":
170 | p = ''
171 | if p not in plugin_list:
172 | if '' in plugin_list and p == ' ':
173 | continue
174 | if p == "all":
175 | continue
176 | msg = "插件 " + str(p) + " 不存在,请重试."
177 | status = bot.sendChatAction(chat_id, "typing")
178 | status = bot.sendMessage(chat_id=chat_id, text=msg, parse_mode="HTML", reply_to_message_id=message_id)
179 | bot.message_deletor(15, chat_id, status["message_id"])
180 | return False
181 | if plug_set == "all":
182 | lock.acquire()
183 | with open(bot.path_converter(plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db"), "w") as f:
184 | f.write('')
185 | lock.release()
186 | status = bot.sendChatAction(chat_id, "typing")
187 | status = bot.sendMessage(chat_id=chat_id, text="已启用全部插件。", parse_mode="HTML", reply_to_message_id=message_id)
188 | bot.message_deletor(15, chat_id, status["message_id"])
189 | return False
190 | elif len(plug_set.split(',')) >= 2:
191 | with open(bot.path_converter(plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db"), "r") as f:
192 | plugin_setting = f.read().strip()
193 | plugin_list_off = plugin_setting.split(',')
194 | for i, plug_s in enumerate(plug_set.split(',')):
195 | if plug_s in plugin_list_off:
196 | for i, p in enumerate(plugin_list_off):
197 | if p == plug_s:
198 | del plugin_list_off[i]
199 | lock.acquire()
200 | with open(bot.path_converter(plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db"), "w") as f:
201 | f.write(','.join(plugin_list_off))
202 | lock.release()
203 | else:
204 | plug_set = plug_set.strip()
205 | with open(bot.path_converter(plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db"), "r") as f:
206 | plugin_setting = f.read().strip()
207 | plugin_list_off = plugin_setting.split(',')
208 | if plug_set in plugin_list_off:
209 | for i, p in enumerate(plugin_list_off):
210 | if p == plug_set:
211 | del plugin_list_off[i]
212 | lock.acquire()
213 | with open(bot.path_converter(plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db"), "w") as f:
214 | f.write(','.join(plugin_list_off))
215 | lock.release()
216 | status = bot.sendChatAction(chat_id, "typing")
217 | status = bot.sendMessage(chat_id=chat_id, text="启用成功!", parse_mode="HTML", reply_to_message_id=message_id)
218 | bot.message_deletor(15, chat_id, status["message_id"])
219 | else:
220 | status = bot.sendChatAction(chat_id, "typing")
221 | status = bot.sendMessage(chat_id=chat_id, text="指令错误,请检查.", parse_mode="HTML", reply_to_message_id=message_id)
222 | bot.message_deletor(15, chat_id, status["message_id"])
223 |
224 | elif text[1:len(prefix + command["/pluginctloff"])+1] == prefix + command["/pluginctloff"]:
225 | default_plugin = ["Menu", "About", "PluginCTL", "Uptime", "Schedule"]
226 | plugin_list = list(plugin_bridge.keys())
227 | if len(text.split(' ')) == 2:
228 | plug_set = text.split(' ')[1]
229 | for p in plug_set.split(','):
230 | if p not in plugin_list:
231 | if p == ' ' or p == '':
232 | continue
233 | if p == "all":
234 | continue
235 | msg = "插件 " + str(p) + " 不存在,请重试."
236 | status = bot.sendChatAction(chat_id, "typing")
237 | status = bot.sendMessage(chat_id=chat_id, text=msg, parse_mode="HTML", reply_to_message_id=message_id)
238 | bot.message_deletor(15, chat_id, status["message_id"])
239 | return False
240 | if type(plug_set) == str and plug_set == "all":
241 | plugin_list_alloff = []
242 | for i, p in enumerate(plugin_list):
243 | if p == "" or p == " ":
244 | p = "nil"
245 | if p not in default_plugin:
246 | plugin_list_alloff.append(p)
247 | lock.acquire()
248 | with open(bot.path_converter(plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db"), "w") as f:
249 | f.write(','.join(plugin_list_alloff))
250 | lock.release()
251 | status = bot.sendChatAction(chat_id, "typing")
252 | status = bot.sendMessage(chat_id=chat_id, text="已禁用全部插件,\n但必须的插件仍被保留。", parse_mode="HTML", reply_to_message_id=message_id)
253 | bot.message_deletor(15, chat_id, status["message_id"])
254 | return False
255 | elif len(plug_set.split(',')) >= 2:
256 | for i, p in enumerate(plug_set.split(',')):
257 | if p in default_plugin:
258 | status = bot.sendChatAction(chat_id, "typing")
259 | msg = "插件 " + str(p) + " 不支持禁用."
260 | status = bot.sendMessage(chat_id=chat_id, text=msg, parse_mode="HTML", reply_to_message_id=message_id)
261 | bot.message_deletor(15, chat_id, status["message_id"])
262 | return False
263 | with open(bot.path_converter(plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db"), "r") as f:
264 | plugin_setting = f.read().strip()
265 | plugin_list_off = plugin_setting.split(',')
266 | for i, plug_s in enumerate(plug_set.split(',')):
267 | if plug_s not in plugin_list_off:
268 | plugin_list_off.append(plug_s)
269 | lock.acquire()
270 | with open(bot.path_converter(plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db"), "w") as f:
271 | f.write(','.join(plugin_list_off))
272 | lock.release()
273 | else:
274 | plug_set = plug_set.strip()
275 | for i, p in enumerate(plug_set.split(',')):
276 | if p in default_plugin:
277 | status = bot.sendChatAction(chat_id, "typing")
278 | msg = "插件 " + str(p) + " 不支持禁用."
279 | status = bot.sendMessage(chat_id=chat_id, text=msg, parse_mode="HTML", reply_to_message_id=message_id)
280 | bot.message_deletor(15, chat_id, status["message_id"])
281 | return False
282 | with open(bot.path_converter(plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db"), "r") as f:
283 | plugin_setting = f.read().strip()
284 | plugin_list_off = plugin_setting.split(',')
285 | if plug_set not in plugin_list_off:
286 | plugin_list_off.append(plug_set)
287 | lock.acquire()
288 | with open(bot.path_converter(plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db"), "w") as f:
289 | f.write(','.join(plugin_list_off))
290 | lock.release()
291 | status = bot.sendChatAction(chat_id, "typing")
292 | status = bot.sendMessage(chat_id=chat_id, text="禁用成功!", parse_mode="HTML", reply_to_message_id=message_id)
293 | bot.message_deletor(15, chat_id, status["message_id"])
294 | else:
295 | status = bot.sendChatAction(chat_id, "typing")
296 | status = bot.sendMessage(chat_id=chat_id, text="指令错误,请检查.", parse_mode="HTML", reply_to_message_id=message_id)
297 | bot.message_deletor(15, chat_id, status["message_id"])
298 |
299 |
300 | else:
301 | status = bot.sendChatAction(chat_id, "typing")
302 | status = bot.sendMessage(chat_id=chat_id, text="指令错误,请检查.", parse_mode="HTML", reply_to_message_id=message_id)
303 | bot.message_deletor(15, chat_id, status["message_id"])
304 |
305 |
306 |
307 | def administrators(bot, chat_id):
308 | admins = []
309 | results = bot.getChatAdministrators(chat_id=chat_id)
310 | if results != False:
311 | for result in results:
312 | if str(result["user"]["is_bot"]) == "False":
313 | admins.append(str(result["user"]["id"]))
314 | else:
315 | admins = False
316 |
317 | return admins
318 |
--------------------------------------------------------------------------------
/teelebot/plugins/PluginCTL/__init__.py:
--------------------------------------------------------------------------------
1 | #/pluginctl
2 | #PluginCTL插件,控制插件开关
--------------------------------------------------------------------------------
/teelebot/plugins/Schedule/Schedule.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | '''
3 | creation time: 2020-11-11
4 | last_modify: 2020-11-14
5 | '''
6 | import time
7 |
8 | def Schedule(bot, message):
9 | chat_id = message["chat"]["id"]
10 | user_id = message["from"]["id"]
11 | message_id = message["message_id"]
12 | text = message["text"]
13 |
14 | root_id = bot.root_id
15 |
16 | gaps = {
17 | "1s": 1,
18 | "2s": 2,
19 | "5s": 5,
20 | "10s": 10,
21 | "15s": 15,
22 | "30s": 30,
23 | "45s": 45,
24 |
25 | "1m": 60,
26 | "2m": 120,
27 | "5m": 300,
28 | "10m": 600,
29 | "15m": 900,
30 | "30m": 1800,
31 | "45m": 2700,
32 |
33 | "1h": 3600,
34 | "2h": 7200,
35 | "4h": 10800,
36 | "6h": 21600,
37 | "8h": 28800,
38 | "10h": 36000,
39 | "12h": 43200,
40 |
41 | "1d": 86400,
42 | "3d": 259200,
43 | "5d": 432000,
44 | "7d": 604800,
45 | "10d": 864000,
46 | "15d": 1296000,
47 | "20d": 1728000,
48 | "30d": 2592000
49 | }
50 |
51 | prefix = "/sched"
52 | command = { #命令注册
53 | "/schedadd": "add",
54 | "/scheddel": "del",
55 | "/schedfind": "find",
56 | "/schedclear": "clear",
57 | "/schedstatus": "status"
58 | }
59 | count = 0
60 | for c in command.keys():
61 | if c in str(text):
62 | count += 1
63 |
64 | if text.split(" ")[0] != prefix and prefix in text and str(user_id) != root_id:
65 | status = bot.sendMessage(chat_id, text="无权限", parse_mode="HTML",
66 | reply_to_message_id=message_id)
67 | bot.message_deletor(15, status["chat"]["id"], status["message_id"])
68 | return
69 |
70 | if text[:len(prefix)] == prefix and count == 0:
71 | msg = "Schedule 插件功能" + "\n\n" + \
72 | "/schedadd 添加任务 格式:指令+空格+周期+消息" + "\n" + \
73 | "/scheddel 移除任务 格式:指令+空格+标识" + "\n" + \
74 | "/schedfind 查找任务 格式:指令+空格+标识" + "\n" + \
75 | "/schedclear 移除所有任务" + "\n" + \
76 | "/schedstatus 查看队列信息" + "\n\n" + \
77 | "支持的周期指令:1s 2s 5s 10s 15s 30s 45s | "+ \
78 | "1m 2m 5m 10m 15m 30m 45m | " + \
79 | "1h 2h 4h 6h 8h 10h 12h | " + \
80 | "1d 3d 5d 7d 10d 15d 20d 30d" + ""
81 | status = bot.sendMessage(chat_id, text=msg, parse_mode="HTML",
82 | reply_to_message_id=message_id)
83 | bot.message_deletor(60, status["chat"]["id"], status["message_id"])
84 |
85 | elif text[:len(prefix + "add")] == prefix + "add":
86 | if len(text.split(" ")) == 3:
87 | msg = ""
88 | gap_key = str(text.split(" ")[1])
89 | if gap_key not in gaps.keys():
90 | msg = "错误的周期,支持的周期指令: \n\n" + \
91 | "1s 2s 5s 10s 15s 30s 45s \n" + \
92 | "1m 2m 5m 10m 15m 30m 45m \n" + \
93 | "1h 2h 4h 6h 8h 10h 12h \n" + \
94 | "1d 3d 5d 7d 10d 15d 20d 30d" + ""
95 | status = bot.sendMessage(chat_id, text=msg, parse_mode="HTML",
96 | reply_to_message_id=message_id)
97 | bot.message_deletor(30, status["chat"]["id"], status["message_id"])
98 | return
99 |
100 | gap = gaps[gap_key]
101 | gap_key = gap_key.replace("s", "秒").replace("m", "分钟").replace("h", "小时").replace("d", "天")
102 | msg = str(text.split(" ")[2]) + "\n\n" + "此消息为定时发送,周期" + str(gap_key) + ""
103 | ok, uid = bot.schedule.add(gap, event, (bot, message["chat"]["id"], msg, "HTML"))
104 | timestamp = time.strftime('%Y/%m/%d %H:%M:%S',time.localtime(time.time()))
105 | if ok:
106 | msg = "任务已加入队列\n\n" + \
107 | "周期: " + gap_key + "\n" + \
108 | "目标: " + str(chat_id) + "\n" + \
109 | "标识: " + str(uid) + "\n" + \
110 | "时间: " + str(timestamp) + "\n\n" + \
111 | "此消息将在60秒后销毁,请尽快保存标识\n"
112 | else:
113 | msg = ""
114 | if uid == "Full":
115 | msg = "队列已满"
116 | else:
117 | msg = "遇到错误 \n\n " + uid + ""
118 | status = bot.sendMessage(chat_id, text=msg, parse_mode="HTML",
119 | reply_to_message_id=message_id)
120 | bot.message_deletor(60, status["chat"]["id"], status["message_id"])
121 | else:
122 | status = bot.sendMessage(chat_id,
123 | text="指令格式错误 (e.g.: " + prefix + "add gap text)",
124 | parse_mode="HTML", reply_to_message_id=message_id)
125 | bot.message_deletor(30, status["chat"]["id"], status["message_id"])
126 |
127 | elif text[:len(prefix + "del")] == prefix + "del":
128 | if len(text.split(" ")) == 2:
129 | msg = ""
130 | uid = str(text.split(" ")[1])
131 | ok, uid = bot.schedule.delete(uid)
132 | if ok:
133 | msg = "移除了任务 " + str(uid) + ""
134 | else:
135 | if uid == "Empty":
136 | msg = "队列为空"
137 | elif uid == "NotFound":
138 | msg = "任务未找到"
139 | status = bot.sendMessage(chat_id, text=msg,
140 | parse_mode="HTML", reply_to_message_id=message_id)
141 | bot.message_deletor(30, status["chat"]["id"], status["message_id"])
142 | else:
143 | status = bot.sendMessage(chat_id,
144 | text="指令格式错误 (e.g.: " + prefix + "del uid)",
145 | parse_mode="HTML", reply_to_message_id=message_id)
146 | bot.message_deletor(30, status["chat"]["id"], status["message_id"])
147 |
148 | elif text[:len(prefix + "find")] == prefix + "find":
149 | if len(text.split(" ")) == 2:
150 | msg = ""
151 | uid = str(text.split(" ")[1])
152 | ok, uid = bot.schedule.find(uid)
153 | if ok:
154 | msg = "任务存在于队列中"
155 | else:
156 | if uid == "Empty":
157 | msg = "队列为空"
158 | elif uid == "NotFound":
159 | msg = "任务未找到"
160 | status = bot.sendMessage(chat_id, text=msg,
161 | parse_mode="HTML", reply_to_message_id=message_id)
162 | bot.message_deletor(30, status["chat"]["id"], status["message_id"])
163 | else:
164 | status = bot.sendMessage(chat_id,
165 | text="指令格式错误 (e.g.: " + prefix + "del uid)",
166 | parse_mode="HTML", reply_to_message_id=message_id)
167 | bot.message_deletor(30, status["chat"]["id"], status["message_id"])
168 |
169 | elif text[:len(prefix + "clear")] == prefix + "clear":
170 | msg = ""
171 | ok, msgg = bot.schedule.clear()
172 | if ok:
173 | msg = "已清空队列"
174 | else:
175 | if msgg == "Empty":
176 | msg = "队列为空"
177 | elif msgg != "Cleared":
178 | msg = "遇到错误 \n\n " + msgg + ""
179 |
180 | status = bot.sendMessage(chat_id, text=msg,
181 | parse_mode="HTML", reply_to_message_id=message_id)
182 | bot.message_deletor(30, status["chat"]["id"], status["message_id"])
183 |
184 | elif text[:len(prefix + "status")] == prefix + "status":
185 | msg = ""
186 | ok, result = bot.schedule.status()
187 | if ok:
188 | msg = "使用: " + str(result["used"]) + "\n" + \
189 | "空闲: " + str(result["free"]) + "\n" + \
190 | "容量: " + str(result["size"]) + "\n"
191 | else:
192 | msg = "遇到错误 \n\n " + result["exception"] + ""
193 | status = bot.sendMessage(chat_id, text=msg,
194 | parse_mode="HTML", reply_to_message_id=message_id)
195 | bot.message_deletor(30, status["chat"]["id"], status["message_id"])
196 |
197 |
198 |
199 | def event(bot, chat_id, msg, parse_mode):
200 | status = bot.sendMessage(chat_id=chat_id, text=msg, parse_mode="HTML")
201 |
202 |
--------------------------------------------------------------------------------
/teelebot/plugins/Schedule/__init__.py:
--------------------------------------------------------------------------------
1 | #/sched
2 | #Schedule插件 周期性执行特定任务,目前仅支持文本类消息
3 |
--------------------------------------------------------------------------------
/teelebot/plugins/Uptime/Uptime.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | '''
3 | creation time: 2020-6-26
4 | last_modify: 2020-11-18
5 | '''
6 | import os
7 | from datetime import timedelta
8 |
9 | def Uptime(bot, message):
10 |
11 | chat_id = message["chat"]["id"]
12 | message_id = message["message_id"]
13 | text = message["text"]
14 | plugin_dir = bot.plugin_dir
15 | VERSION = bot.version
16 | prefix = "uptime"
17 |
18 | if not os.path.exists(bot.path_converter(plugin_dir + "Uptime/config.ini")):
19 | detail_links = None
20 | else:
21 | with open(bot.path_converter(plugin_dir + "Uptime/config.ini"), 'r') as f:
22 | detail_links = f.readline().strip()
23 |
24 | if text[1:len(prefix)+1] == prefix:
25 | time_second = bot.uptime
26 | time_format = timedelta(seconds=time_second)
27 | response_times = bot.response_times
28 | response_chats = len(bot.response_chats)
29 | response_users = len(bot.response_users)
30 | status = bot.sendChatAction(chat_id, "typing")
31 | inlineKeyboard = [
32 | [
33 | {"text": "详细信息", "url": detail_links}
34 | ]
35 | ]
36 | if detail_links is not None:
37 | reply_markup = {
38 | "inline_keyboard": inlineKeyboard
39 | }
40 | else:
41 | reply_markup = None
42 | msg = "感谢您的关心 ( ̄ε  ̄) \n\n我已经运行 " + str(time_second) + " 秒\n" +\
43 | "即:" + str(time_format) + "\n\n" +\
44 | "在此期间:\n" +\
45 | "响应指令 " + str(response_times) + " 次\n" +\
46 | "服务群组 " + str(response_chats) + " 个\n" +\
47 | "服务用户 " + str(response_users) + " 名\n\n" +\
48 | "v" + str(VERSION) + ""
49 |
50 | status = bot.sendMessage(chat_id=chat_id, text=msg, parse_mode="HTML", reply_to_message_id=message_id, reply_markup=reply_markup)
51 | bot.message_deletor(15, chat_id, status["message_id"])
--------------------------------------------------------------------------------
/teelebot/plugins/Uptime/__init__.py:
--------------------------------------------------------------------------------
1 | #/uptime
2 | # 查看Bot运行状态
--------------------------------------------------------------------------------
/teelebot/plugins/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Everless321/telebot-invitecode/9ebbf77e85620485574b7bebd61d247e9a1d6612/teelebot/plugins/__init__.py
--------------------------------------------------------------------------------
/teelebot/polling.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | '''
3 | @creation date: 2020-6-23
4 | @last modify: 2020-11-23
5 | '''
6 | import time
7 | import sys
8 |
9 |
10 | def _runUpdates(bot):
11 | plugin_bridge = bot.plugin_bridge
12 | plugin_list = plugin_bridge.keys()
13 | try:
14 | while True:
15 | results = bot.getUpdates() # 获取消息队列messages
16 | messages = bot._washUpdates(results)
17 | if messages is None or not messages:
18 | continue
19 | for message in messages: # 获取单条消息message
20 | bot._pluginRun(bot, message)
21 | except KeyboardInterrupt:
22 | sys.exit("Bot Exit.") # 退出存在问题,待修复
23 |
--------------------------------------------------------------------------------
/teelebot/request.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | '''
3 | @creation date: 2019-11-15
4 | @last modify: 2020-11-23
5 | '''
6 | import os
7 |
8 | import requests
9 | import inspect
10 | from .logger import _logger
11 | from traceback import extract_stack
12 | from requests.packages.urllib3.exceptions import InsecureRequestWarning
13 |
14 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
15 |
16 |
17 | class _Request(object):
18 | """
19 | 接口请求类
20 | """
21 | def __init__(self, thread_pool_size, url, debug=False):
22 | self.__url = url
23 | self.__debug = debug
24 | self.__session = self.__connection_session(
25 | pool_connections=thread_pool_size,
26 | pool_maxsize=thread_pool_size * 2
27 | )
28 |
29 | def __del__(self):
30 | self.__session.close()
31 |
32 | def __connection_session(self, pool_connections=10, pool_maxsize=10, max_retries=5):
33 | """
34 | 连接池
35 | """
36 | session = requests.Session()
37 | session.verify = False
38 |
39 | adapter = requests.adapters.HTTPAdapter(pool_connections=pool_connections,
40 | pool_maxsize=pool_maxsize, max_retries=max_retries)
41 | session.mount('http://', adapter)
42 | session.mount('https://', adapter)
43 |
44 | return session
45 |
46 | def __debug_info(self, result):
47 | """
48 | debug模式
49 | """
50 | if self.__debug and not result.get("ok"):
51 | os.system("") # "玄学"解决Windows下颜色显示失效的问题...
52 | stack_info = extract_stack()
53 | if len(stack_info) > 8: # 插件内
54 | _logger.debug("\033[1;31m" + \
55 | "Request failed" + " - " + \
56 | "From:" + stack_info[-3][2] + " - " + \
57 | "Path:" + stack_info[5][0] + " - " + \
58 | "Line:" + str(stack_info[5][1]) + " - " + \
59 | "Method:" + stack_info[6][2] + " - " + \
60 | "Result:" + str(result) + \
61 | "\033[0m")
62 | elif len(stack_info) > 3: # 外部调用
63 | _logger.debug("\033[1;31m" + \
64 | "Request failed" + " - " + \
65 | "From:" + stack_info[0][0] + " - " + \
66 | "Path:" + stack_info[1][0] + " - " + \
67 | "Line:" + str(stack_info[0][1]) + " - " + \
68 | "Method:" + stack_info[1][2] + " - " + \
69 | "Result:" + str(result) + \
70 | "\033[0m")
71 |
72 | def post(self, addr):
73 | try:
74 | with self.__session.post(self.__url + addr) as req:
75 | self.__debug_info(req.json())
76 | if req.json().get("ok"):
77 | return req.json().get("result")
78 | elif not req.json().get("ok"):
79 | return req.json().get("ok")
80 | except:
81 | return False
82 |
83 | def postFile(self, addr, file_data):
84 | try:
85 | with self.__session.post(self.__url + addr, files=file_data) as req:
86 | self.__debug_info(req.json())
87 | if req.json().get("ok"):
88 | return req.json().get("result")
89 | elif not req.json().get("ok"):
90 | return req.json().get("ok")
91 | except:
92 | return False
93 |
94 | def postJson(self, addr, json):
95 | try:
96 | with self.__session.get(self.__url + addr, json=json) as req:
97 | self.__debug_info(req.json())
98 | if req.json().get("ok"):
99 | return req.json().get("result")
100 | elif not req.json().get("ok"):
101 | return req.json().get("ok")
102 | except:
103 | return False
104 |
105 | def get(self, addr):
106 | try:
107 | with self.__session.get(self.__url + addr) as req:
108 | self.__debug_info(req.json())
109 | if req.json().get("ok"):
110 | return req.json().get("result")
111 | elif not req.json().get("ok"):
112 | return req.json().get("ok")
113 | except:
114 | return False
115 |
116 |
117 |
--------------------------------------------------------------------------------
/teelebot/schedule.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | '''
3 | @creation date: 2019-11-15
4 | @last modify: 2020-11-18
5 | '''
6 | import threading
7 | from uuid import uuid4
8 |
9 | class _Schedule(object):
10 | """
11 | 周期性任务类
12 | """
13 | def __init__(self, queue_size):
14 | self.__queue_size = queue_size
15 | self.__queue_mutex = threading.Lock()
16 | self.__queue = {}
17 |
18 | def __create(self, gap, func, args):
19 | class RepeatingTimer(threading.Timer):
20 | def run(self):
21 | while not self.finished.is_set():
22 | self.function(*self.args, **self.kwargs)
23 | self.finished.wait(self.interval)
24 | try:
25 | t = RepeatingTimer(gap, func, args)
26 | t.setDaemon(True)
27 | return True, t
28 | except Exception as e:
29 | print(e)
30 | return False, str(e)
31 |
32 | def add(self, gap, func, args):
33 | """
34 | 添加周期性任务
35 | """
36 | def __short_uuid():
37 | uuidChars = ("a", "b", "c", "d", "e", "f",
38 | "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s",
39 | "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5",
40 | "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "I",
41 | "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
42 | "W", "X", "Y", "Z")
43 | uuid = str(uuid4().hex)
44 | uid = ''
45 | for i in range(0,8):
46 | sub = uuid[i * 4: i * 4 + 4]
47 | x = int(sub,16)
48 | uid += uuidChars[x % 0x3E]
49 | return uid
50 |
51 | if len(self.__queue) == self.__queue_size:
52 | return False, "Full"
53 |
54 | ok, t = self.__create(gap, func, args)
55 | if ok:
56 | t.start()
57 | uid = __short_uuid()
58 | with self.__queue_mutex:
59 | self.__queue[uid] = t
60 |
61 | return True, uid
62 | else:
63 | return False, t
64 |
65 | def status(self):
66 | """
67 | 获取周期性任务池的使用情况
68 | """
69 | try:
70 | used = len(self.__queue)
71 | free = self.__queue_size - used
72 | size = self.__queue_size
73 |
74 | result = {
75 | "used": used,
76 | "free": free,
77 | "size": size
78 | }
79 | return True, result
80 | except Exception as e:
81 | return False, {"exception": e}
82 |
83 | def find(self, uid):
84 | """
85 | 查找周期性任务
86 | """
87 | if len(self.__queue) <= 0:
88 | return False, "Empty"
89 |
90 | if str(uid) in self.__queue.keys():
91 | return True, str(uid)
92 | else:
93 | return False, "NotFound"
94 |
95 | def delete(self, uid):
96 | """
97 | 移除周期性任务
98 | """
99 | if len(self.__queue) <= 0:
100 | return False, "Empty"
101 |
102 | if str(uid) in self.__queue.keys():
103 | self.__queue[str(uid)].cancel()
104 | with self.__queue_mutex:
105 | self.__queue.pop(str(uid))
106 |
107 | return True, str(uid)
108 | else:
109 | return False, "NotFound"
110 |
111 | def clear(self):
112 | """
113 | 移除所有周期性任务
114 | """
115 | if len(self.__queue) == 0:
116 | return False, "Empty"
117 | else:
118 | try:
119 | for uid in list(self.__queue.keys()):
120 | self.__queue[str(uid)].cancel()
121 |
122 | with self.__queue_mutex:
123 | self.__queue.clear()
124 |
125 | return True, "Cleared"
126 | except Exception as e:
127 | return False, str(e)
128 |
--------------------------------------------------------------------------------
/teelebot/teelebot.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | """
3 | @description:基于Telegram Bot Api 的机器人框架
4 | @creation date: 2019-8-13
5 | @last modify: 2020-11-27
6 | @author: Pluto (github:plutobell)
7 | @version: 1.14.1
8 | """
9 | import inspect
10 | import time
11 | import sys
12 | import os
13 | import json
14 | import shutil
15 | import importlib
16 | import threading
17 |
18 | from pathlib import Path
19 | from urllib.parse import quote
20 | from concurrent.futures import ThreadPoolExecutor
21 |
22 | from .handler import _config, _bridge, _plugin_info
23 | from .logger import _logger
24 | from .schedule import _Schedule
25 | from .request import _Request
26 |
27 |
28 | class Bot(object):
29 | """机器人的基类"""
30 |
31 | def __init__(self, key=""):
32 | config = _config()
33 |
34 | if key != "":
35 | self._key = key
36 | elif key == "":
37 | self._key = config["key"]
38 |
39 | self._cloud_api_server = config["cloud_api_server"]
40 | self._local_api_server = config["local_api_server"]
41 | if self._local_api_server != "False":
42 | self._basic_url = config["local_api_server"]
43 | else:
44 | self._basic_url = self._cloud_api_server
45 | self._url = self._basic_url + r"bot" + self._key + r"/"
46 |
47 | self._webhook = config["webhook"]
48 | if self._webhook:
49 | self._self_signed = config["self_signed"]
50 | self._cert_key = config["cert_key"]
51 | self._cert_pub = config["cert_pub"]
52 | self._server_address = config["server_address"]
53 | self._server_port = config["server_port"]
54 | self._local_address = config["local_address"]
55 | self._local_port = config["local_port"]
56 | self._offset = 0
57 | self._timeout = 60
58 | self._debug = config["debug"]
59 | self._pool_size = config["pool_size"]
60 | self._drop_pending_updates = config["drop_pending_updates"]
61 |
62 | self.__root_id = config["root_id"]
63 | self.__bot_id = self._key.split(":")[0]
64 | self.__AUTHOR = config["author"]
65 | self.__VERSION = config["version"]
66 | self.__plugin_dir = config["plugin_dir"]
67 | self.__plugin_bridge = config["plugin_bridge"]
68 | self.__start_time = int(time.time())
69 | self.__response_times = 0
70 | self.__response_chats = []
71 | self.__response_users = []
72 |
73 | thread_pool_size = round(int(self._pool_size) * 2 / 3)
74 | schedule_queue_size = int(self._pool_size) - thread_pool_size
75 | self.request = _Request(thread_pool_size, self._url, self._debug)
76 | self.schedule = _Schedule(schedule_queue_size)
77 |
78 | self.__thread_pool = ThreadPoolExecutor(
79 | max_workers=thread_pool_size)
80 | self.__timer_thread_pool = ThreadPoolExecutor(
81 | max_workers=int(self._pool_size) * 5)
82 |
83 | self.__plugin_info = config["plugin_info"]
84 |
85 | del config
86 | del thread_pool_size
87 | del schedule_queue_size
88 |
89 | def __del__(self):
90 | self.__thread_pool.shutdown(wait=True)
91 | self.__timer_thread_pool.shutdown(wait=True)
92 | del self.request
93 | del self.schedule
94 |
95 | # teelebot method
96 | def __threadpool_exception(self, fur):
97 | """
98 | 线程池异常回调
99 | """
100 | if fur.exception() is not None:
101 | _logger.debug("EXCEPTION" + " - " + str(fur.result()))
102 |
103 | def __import_module(self, plugin_name):
104 | """
105 | 动态导入模块
106 | """
107 | sys.path.append(self.path_converter(self.__plugin_dir + plugin_name + os.sep))
108 | Module = importlib.import_module(plugin_name) # 模块检测
109 |
110 | return Module
111 |
112 | def __update_plugin(self, plugin_name):
113 | """
114 | 热更新插件
115 | """
116 | plugin_uri = self.path_converter(
117 | self.__plugin_dir + plugin_name + os.sep + plugin_name + ".py")
118 | now_mtime = os.stat(plugin_uri).st_mtime
119 | # print(now_mtime, self.__plugin_info[plugin_name])
120 | if now_mtime != self.__plugin_info[plugin_name]: # 插件热更新
121 | if os.path.exists(self.path_converter(self.__plugin_dir + plugin_name + r"/__pycache__")):
122 | shutil.rmtree(self.path_converter(self.__plugin_dir + plugin_name + r"/__pycache__"))
123 | self.__plugin_info[plugin_name] = now_mtime
124 | Module = self.__import_module(plugin_name)
125 | importlib.reload(Module)
126 | _logger.info("The plugin " + plugin_name + " has been updated")
127 |
128 | def __load_plugin(self, now_plugin_bridge, now_plugin_info):
129 | """
130 | 动态装载插件
131 | """
132 | for plugin in list(now_plugin_bridge.keys()):
133 | if plugin not in list(self.__plugin_bridge.keys()):
134 | _logger.info("The plugin " + plugin + " has been installed")
135 | self.__plugin_info[plugin] = now_plugin_info[plugin]
136 | for plugin in list(self.__plugin_bridge.keys()):
137 | if plugin not in list(now_plugin_bridge.keys()):
138 | _logger.info("The plugin " + plugin + " has been uninstalled")
139 | self.__plugin_info.pop(plugin)
140 |
141 | self.__plugin_bridge = now_plugin_bridge
142 |
143 | def __control_plugin(self, plugin_bridge, chat_type, chat_id):
144 | if chat_type != "private" and "PluginCTL" in plugin_bridge.keys() \
145 | and plugin_bridge["PluginCTL"] == "/pluginctl":
146 | if os.path.exists(self.path_converter(self.__plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db")):
147 | with open(self.path_converter(self.__plugin_dir + "PluginCTL/db/" + str(chat_id) + ".db"), "r") as f:
148 | plugin_setting = f.read().strip()
149 | plugin_list_off = plugin_setting.split(',')
150 | plugin_bridge_temp = {}
151 | for plugin in list(plugin_bridge.keys()):
152 | if plugin not in plugin_list_off:
153 | plugin_bridge_temp[plugin] = plugin_bridge[plugin]
154 | plugin_bridge = plugin_bridge_temp
155 |
156 | return plugin_bridge
157 |
158 | def __mark_message_for_pluginRun(self, message):
159 | if "callback_query_id" in message.keys(): # callback query
160 | message["message_type"] = "callback_query_data"
161 | message_type = "callback_query_data"
162 | elif ("new_chat_members" in message.keys()) or ("left_chat_member" in message.keys()):
163 | message["message_type"] = "text"
164 | message_type = "text"
165 | message["text"] = "" # default prefix of command
166 | elif "photo" in message.keys():
167 | message["message_type"] = "photo"
168 | message_type = "message_type"
169 | elif "sticker" in message.keys():
170 | message["message_type"] = "sticker"
171 | message_type = "message_type"
172 | elif "video" in message.keys():
173 | message["message_type"] = "video"
174 | message_type = "message_type"
175 | elif "audio" in message.keys():
176 | message["message_type"] = "audio"
177 | message_type = "message_type"
178 | elif "document" in message.keys():
179 | message["message_type"] = "document"
180 | message_type = "message_type"
181 | elif "text" in message.keys():
182 | message["message_type"] = "text"
183 | message_type = "text"
184 | elif "caption" in message.keys():
185 | message["message_type"] = "caption"
186 | message_type = "caption"
187 | elif "query" in message.keys():
188 | message["message_type"] = "query"
189 | message_type = "query"
190 | else:
191 | message["message_type"] = "unknown"
192 | message_type = "unknown"
193 |
194 | return message_type, message
195 |
196 | def __logging_for_pluginRun(self, message, plugin):
197 | title = "" # INFO日志
198 | user_name = ""
199 |
200 | if message["chat"]["type"] == "private":
201 | if "first_name" in message["chat"].keys():
202 | title += message["chat"]["first_name"]
203 | if "last_name" in message["chat"].keys():
204 | if "first_name" in message["chat"].keys():
205 | title += " " + message["chat"]["last_name"]
206 | else:
207 | title += message["chat"]["last_name"]
208 | elif "title" in message["chat"].keys():
209 | title = message["chat"]["title"]
210 | if "reply_markup" in message.keys() and \
211 | message["message_type"] == "callback_query_data":
212 | from_id = message["click_user"]["id"]
213 | if "first_name" in message["click_user"].keys():
214 | user_name += message["click_user"]["first_name"]
215 | if "last_name" in message["click_user"].keys():
216 | if "first_name" in message["click_user"].keys():
217 | user_name += " " + message["click_user"]["last_name"]
218 | else:
219 | user_name += message["chat"]["last_name"]
220 | else:
221 | from_id = message["from"]["id"]
222 | if "first_name" in message["from"].keys():
223 | user_name += message["from"]["first_name"]
224 | if "last_name" in message["from"].keys():
225 | if "first_name" in message["from"].keys():
226 | user_name += " " + message["from"]["last_name"]
227 | else:
228 | user_name += message["from"]["last_name"]
229 |
230 | if message["message_type"] == "unknown":
231 | _logger.info(
232 | "From:" + title + "(" + str(message["chat"]["id"]) + ") - " + \
233 | "User:" + user_name + "(" + str(from_id) + ") - " + \
234 | "Plugin: " + "" + " - " + \
235 | "Type:" + message["message_type"])
236 | else:
237 | _logger.info(
238 | "From:" + title + "(" + str(message["chat"]["id"]) + ") - " + \
239 | "User:" + user_name + "(" + str(from_id) + ") - " + \
240 | "Plugin: " + str(plugin) + " - " + \
241 | "Type:" + message["message_type"])
242 |
243 | def _pluginRun(self, bot, message):
244 | """
245 | 运行插件
246 | """
247 | if message is None:
248 | return
249 |
250 | now_plugin_bridge = _bridge(self.__plugin_dir)
251 | now_plugin_info = _plugin_info(now_plugin_bridge.keys(), self.__plugin_dir)
252 |
253 | if now_plugin_bridge != self.__plugin_bridge: # 动态装载插件
254 | self.__load_plugin(now_plugin_bridge, now_plugin_info)
255 |
256 | if len(now_plugin_info) != len(self.__plugin_info) or \
257 | now_plugin_info != self.__plugin_info: # 动态更新插件信息
258 | for plugin_name in list(self.__plugin_bridge.keys()):
259 | self.__update_plugin(plugin_name) #热更新插件
260 |
261 | if len(self.__plugin_bridge) == 0:
262 | os.system("")
263 | _logger.warn("\033[1;31mNo plugins installed\033[0m")
264 |
265 | plugin_bridge = self.__control_plugin( # pluginctl控制
266 | self.__plugin_bridge, message["chat"]["type"], message["chat"]["id"])
267 |
268 | message_type = ""
269 | message_type, message = self.__mark_message_for_pluginRun(message) # 分类标记消息
270 |
271 | if message_type == "unknown":
272 | self.__logging_for_pluginRun(message, "unknown")
273 | return
274 |
275 | for plugin, command in plugin_bridge.items():
276 | if message.get(message_type)[:len(command)] == command:
277 | module = self.__import_module(plugin)
278 | pluginFunc = getattr(module, plugin)
279 | fur = self.__thread_pool.submit(pluginFunc, bot, message)
280 | fur.add_done_callback(self.__threadpool_exception)
281 |
282 | self.__response_times += 1
283 |
284 | if message["chat"]["type"] != "private" and \
285 | message["chat"]["id"] not in self.__response_chats:
286 | self.__response_chats.append(message["chat"]["id"])
287 | if message["from"]["id"] not in self.__response_users:
288 | self.__response_users.append(message["from"]["id"])
289 |
290 | self.__logging_for_pluginRun(message, plugin)
291 |
292 | def _washUpdates(self, results):
293 | """
294 | 清洗消息队列
295 | results应当是一个列表
296 | """
297 | if not results:
298 | return False
299 | elif len(results) < 1:
300 | return None
301 | update_ids = []
302 | messages = []
303 | for result in results:
304 | if "update_id" not in result.keys():
305 | return None
306 | update_ids.append(result["update_id"])
307 | query_or_message = ""
308 | if result.get("inline_query"):
309 | query_or_message = "inline_query"
310 | elif result.get("callback_query"):
311 | query_or_message = "callback_query"
312 | elif result.get("message"):
313 | query_or_message = "message"
314 | update_ids.append(result.get("update_id"))
315 |
316 | if query_or_message == "callback_query":
317 | callback_query = result.get(query_or_message).get("message")
318 | callback_query["click_user"] = result.get(query_or_message)[
319 | "from"]
320 | callback_query["callback_query_id"] = result.get(
321 | query_or_message).get("id")
322 | callback_query["callback_query_data"] = result.get(
323 | query_or_message).get("data")
324 | messages.append(callback_query)
325 | else:
326 | messages.append(result.get(query_or_message))
327 | if len(update_ids) >= 1:
328 | self._offset = max(update_ids) + 1
329 | return messages
330 | else:
331 | return None
332 |
333 | def message_deletor(self, time_gap, chat_id, message_id):
334 | """
335 | 定时删除一条消息,时间范围:[0, 900],单位秒
336 | """
337 | if time_gap < 0 or time_gap > 900:
338 | return "time_gap_error"
339 | else:
340 | def message_deletor_func(time_gap, chat_id, message_id):
341 | time.sleep(int(time_gap))
342 | self.deleteMessage(chat_id=chat_id, message_id=message_id)
343 |
344 | if time_gap == 0:
345 | message_deletor_func(chat_id, message_id)
346 | else:
347 | fur = self.__timer_thread_pool.submit(
348 | message_deletor_func, time_gap, chat_id, message_id)
349 | fur.add_done_callback(self.__threadpool_exception)
350 |
351 | return "ok"
352 |
353 | def timer(self, time_gap, func, args):
354 | """
355 | 单次定时器,时间范围:[0, 900],单位秒
356 | """
357 | if time_gap < 0 or time_gap > 900:
358 | return "time_gap_error"
359 | elif type(args) is not tuple:
360 | return "args_must_be_tuple"
361 | else:
362 | def timer_func(time_gap, func, args):
363 | time.sleep(int(time_gap))
364 | func(*args)
365 |
366 | if time_gap == 0:
367 | func(args)
368 | else:
369 | fur = self.__timer_thread_pool.submit(
370 | timer_func, time_gap, func, args)
371 | fur.add_done_callback(self.__threadpool_exception)
372 |
373 | return "ok"
374 |
375 | def path_converter(self, path):
376 | """
377 | 根据操作系统转换URI
378 | """
379 |
380 | path = str(Path(path))
381 |
382 | return path
383 |
384 | @property
385 | def plugin_bridge(self):
386 | """
387 | 获取插件桥
388 | """
389 |
390 | return self.__plugin_bridge
391 |
392 | @property
393 | def plugin_dir(self):
394 | """
395 | 获取插件路径
396 | """
397 |
398 | return self.__plugin_dir
399 |
400 | @property
401 | def version(self):
402 | """
403 | 获取框架版本号
404 | """
405 |
406 | return self.__VERSION
407 |
408 | @property
409 | def author(self):
410 | """
411 | 作者信息
412 | """
413 |
414 | return self.__AUTHOR
415 |
416 | @property
417 | def root_id(self):
418 | """
419 | 获取root用户ID
420 | """
421 |
422 | return self.__root_id
423 |
424 | @property
425 | def bot_id(self):
426 | """
427 | 获取Bot的ID
428 | """
429 |
430 | return self.__bot_id
431 |
432 | @property
433 | def uptime(self):
434 | """
435 | 获取框架的持续运行时间(单位为秒)
436 | """
437 | second = int(time.time()) - self.__start_time
438 |
439 | return second
440 |
441 | @property
442 | def response_times(self):
443 | """
444 | 获取框架启动后响应指令的统计次数
445 | """
446 | return self.__response_times
447 |
448 | @property
449 | def response_chats(self):
450 | """
451 | 获取框架启动后响应的所有群组ID
452 | """
453 | return self.__response_chats
454 |
455 | @property
456 | def response_users(self):
457 | """
458 | 获取框架启动后响应的所有用户ID
459 | """
460 | return self.__response_users
461 |
462 | def getChatCreator(self, chat_id):
463 | """
464 | 获取群组创建者信息
465 | """
466 | if str(chat_id)[0] == "-":
467 | req = self.getChatAdministrators(str(chat_id))
468 | if req:
469 | creator = []
470 | for i, user in enumerate(req):
471 | if user["status"] == "creator":
472 | creator.append(req[i])
473 | if len(creator) == 1:
474 | return creator[0]
475 | else:
476 | return False
477 | else:
478 | return False
479 |
480 | def getFileDownloadPath(self, file_id):
481 | """
482 | 生成文件下载链接
483 | 注意:下载链接包含Bot Key
484 | """
485 | req = self.getFile(file_id=file_id)
486 | if req:
487 | file_path = req["file_path"]
488 | if (self._local_api_server != "False" and
489 | "telegram.org" not in self._basic_url):
490 | return file_path
491 | else:
492 | file_download_path = self._basic_url + "file/bot" + self._key + r"/" + file_path
493 | return file_download_path
494 | else:
495 | return False
496 |
497 | # Getting updates
498 | def getUpdates(self, limit=100, allowed_updates=None):
499 | """
500 | 获取消息队列
501 | """
502 | command = inspect.stack()[0].function
503 | addr = command + "?offset=" + str(self._offset) + \
504 | "&limit=" + str(limit) + "&timeout=" + str(self._timeout)
505 |
506 | if allowed_updates is not None:
507 | return self.request.postJson(addr, allowed_updates)
508 | else:
509 | return self.request.get(addr)
510 |
511 | def setWebhook(self, url, certificate=None, ip_address=None,
512 | max_connections=None, allowed_updates=None, drop_pending_updates=None):
513 | """
514 | 设置Webhook
515 | Ports currently supported for Webhooks: 443, 80, 88, 8443.
516 | """
517 | command = inspect.stack()[0].function
518 | addr = command + "?url=" + str(url)
519 | if ip_address is not None:
520 | addr += "&ip_address=" + str(ip_address)
521 | if max_connections is not None:
522 | addr += "&max_connections=" + str(max_connections)
523 | if allowed_updates is not None:
524 | addr += "&allowed_updates=" + str(allowed_updates)
525 | if drop_pending_updates is not None:
526 | addr += "&drop_pending_updates=" + str(drop_pending_updates)
527 |
528 | file_data = None
529 | if certificate is not None:
530 | if type(certificate) == bytes:
531 | file_data = {"certificate": certificate}
532 | else:
533 | file_data = {"certificate": open(certificate, 'rb')}
534 |
535 | if file_data is None:
536 | return self.request.post(addr)
537 | else:
538 | return self.request.postFile(addr, file_data)
539 |
540 | def deleteWebhook(self, drop_pending_updates=None):
541 | """
542 | 删除设置的Webhook
543 | """
544 | command = inspect.stack()[0].function
545 | addr = command
546 | if drop_pending_updates is not None:
547 | addr += "?drop_pending_updates=" + str(drop_pending_updates)
548 | return self.request.post(addr)
549 |
550 | def getWebhookInfo(self):
551 | """
552 | 获取当前的Webhook状态
553 | """
554 | command = inspect.stack()[0].function
555 | addr = command
556 | return self.request.post(addr)
557 |
558 | # Available methods
559 |
560 | def getMe(self):
561 | """
562 | 获取机器人基本信息
563 | """
564 | command = inspect.stack()[0].function
565 | addr = command + "?" + "offset=" + \
566 | str(self._offset) + "&timeout=" + str(self._timeout)
567 | return self.request.post(addr)
568 |
569 | def getFile(self, file_id):
570 | """
571 | 获取文件信息
572 | """
573 | command = inspect.stack()[0].function
574 | addr = command + "?file_id=" + file_id
575 | return self.request.post(addr)
576 |
577 | def logOut(self):
578 | """
579 | 在本地启动机器人之前,使用此方法从云Bot API服务器注销。
580 | """
581 | command = inspect.stack()[0].function
582 | addr = command
583 |
584 | return self.request.post(addr)
585 |
586 | def close(self):
587 | """
588 | 在将bot实例从一个本地服务器移动到另一个本地服务器之前
589 | 使用此方法关闭它
590 | """
591 | command = inspect.stack()[0].function
592 | addr = command
593 |
594 | return self.request.post(addr)
595 |
596 | def sendMessage(self, chat_id, text, parse_mode="Text", reply_to_message_id=None,
597 | reply_markup=None, disable_web_page_preview=None, entities=None,
598 | allow_sending_without_reply=None):
599 | """
600 | 发送文本消息
601 | """
602 | command = inspect.stack()[0].function
603 | addr = command + "?chat_id=" + str(chat_id) + "&text=" + quote(text)
604 | if parse_mode in ("Markdown", "MarkdownV2", "HTML"):
605 | addr += "&parse_mode=" + parse_mode
606 | if reply_to_message_id is not None:
607 | addr += "&reply_to_message_id=" + str(reply_to_message_id)
608 | if reply_markup is not None:
609 | addr += "&reply_markup=" + json.dumps(reply_markup)
610 | if disable_web_page_preview is not None:
611 | addr += "&disable_web_page_preview=" + str(disable_web_page_preview)
612 | if entities is not None:
613 | addr += "&entities=" + json.dumps(entities)
614 | if allow_sending_without_reply is not None:
615 | addr += "&allow_sending_without_reply=" + str(allow_sending_without_reply)
616 |
617 | return self.request.post(addr)
618 |
619 | def sendVoice(self, chat_id, voice, caption=None, parse_mode="Text", reply_to_message_id=None,
620 | reply_markup=None, allow_sending_without_reply=None, caption_entities=None):
621 | """
622 | 发送音频消息 .ogg
623 | """
624 | command = inspect.stack()[0].function
625 | if voice[:7] == "http://" or voice[:7] == "https:/":
626 | file_data = None
627 | addr = command + "?chat_id=" + str(chat_id) + "&voice=" + voice
628 | elif type(voice) == bytes:
629 | file_data = {"voice": voice}
630 | addr = command + "?chat_id=" + str(chat_id)
631 | elif type(voice) == str and '.' not in voice:
632 | file_data = None
633 | addr = command + "?chat_id=" + str(chat_id) + "&voice=" + voice
634 | else:
635 | file_data = {"voice": open(voice, 'rb')}
636 | addr = command + "?chat_id=" + str(chat_id)
637 |
638 | if caption is not None:
639 | addr += "&caption=" + quote(caption)
640 | if parse_mode in ("Markdown", "MarkdownV2", "HTML"):
641 | addr += "&parse_mode" + parse_mode
642 | if reply_to_message_id is not None:
643 | addr += "&reply_to_message_id=" + str(reply_to_message_id)
644 | if reply_markup is not None:
645 | addr += "&reply_markup=" + json.dumps(reply_markup)
646 | if allow_sending_without_reply is not None:
647 | addr += "&allow_sending_without_reply=" + str(allow_sending_without_reply)
648 | if caption_entities is not None:
649 | addr += "&caption_entities=" + json.dumps(caption_entities)
650 |
651 | if file_data is None:
652 | return self.request.post(addr)
653 | else:
654 | return self.request.postFile(addr, file_data)
655 |
656 | def sendAnimation(self, chat_id, animation, caption=None, parse_mode="Text", reply_to_message_id=None,
657 | reply_markup=None, allow_sending_without_reply=None, caption_entities=None):
658 | """
659 | 发送动画 gif/mp4
660 | """
661 | command = inspect.stack()[0].function
662 | if animation[:7] == "http://" or animation[:7] == "https:/":
663 | file_data = None
664 | addr = command + "?chat_id=" + str(chat_id) + "&animation=" + animation
665 | elif type(animation) == bytes:
666 | file_data = {"animation": animation}
667 | addr = command + "?chat_id=" + str(chat_id)
668 | elif type(animation) == str and '.' not in animation:
669 | file_data = None
670 | addr = command + "?chat_id=" + str(chat_id) + "&animation=" + animation
671 | else:
672 | file_data = {"animation": open(animation, 'rb')}
673 | addr = command + "?chat_id=" + str(chat_id)
674 |
675 | if caption is not None:
676 | addr += "&caption=" + quote(caption)
677 | if parse_mode in ("Markdown", "MarkdownV2", "HTML"):
678 | addr += "&parse_mode" + parse_mode
679 | if reply_to_message_id is not None:
680 | addr += "&reply_to_message_id=" + str(reply_to_message_id)
681 | if reply_markup is not None:
682 | addr += "&reply_markup=" + json.dumps(reply_markup)
683 | if allow_sending_without_reply is not None:
684 | addr += "&allow_sending_without_reply=" + str(allow_sending_without_reply)
685 | if caption_entities is not None:
686 | addr += "&caption_entities=" + json.dumps(caption_entities)
687 |
688 | if file_data is None:
689 | return self.request.post(addr)
690 | else:
691 | self.request.postFile(addr, file_data)
692 |
693 | def sendAudio(self, chat_id, audio, caption=None, parse_mode="Text", title=None, reply_to_message_id=None,
694 | reply_markup=None, allow_sending_without_reply=None, caption_entities=None):
695 | """
696 | 发送音频 mp3
697 | """
698 | command = inspect.stack()[0].function
699 | if audio[:7] == "http://" or audio[:7] == "https:/":
700 | file_data = None
701 | addr = command + "?chat_id=" + str(chat_id) + "&audio=" + audio
702 | elif type(audio) == bytes:
703 | file_data = {"audio": audio}
704 | addr = command + "?chat_id=" + str(chat_id)
705 | elif type(audio) == str and '.' not in audio:
706 | file_data = None
707 | addr = command + "?chat_id=" + str(chat_id) + "&audio=" + audio
708 | else:
709 | file_data = {"audio": open(audio, 'rb')}
710 | addr = command + "?chat_id=" + str(chat_id)
711 |
712 | if caption is not None:
713 | addr += "&caption=" + quote(caption)
714 | if parse_mode in ("Markdown", "MarkdownV2", "HTML"):
715 | addr += "&parse_mode" + parse_mode
716 | if title is not None:
717 | addr += "&title=" + title
718 | if reply_to_message_id is not None:
719 | addr += "&reply_to_message_id=" + str(reply_to_message_id)
720 | if reply_markup is not None:
721 | addr += "&reply_markup=" + json.dumps(reply_markup)
722 | if allow_sending_without_reply is not None:
723 | addr += "&allow_sending_without_reply=" + str(allow_sending_without_reply)
724 | if caption_entities is not None:
725 | addr += "&caption_entities=" + json.dumps(caption_entities)
726 |
727 | if file_data is None:
728 | return self.request.post(addr)
729 | else:
730 | return self.request.postFile(addr, file_data)
731 |
732 | def sendPhoto(self, chat_id, photo, caption=None, parse_mode="Text", reply_to_message_id=None,
733 | reply_markup=None, allow_sending_without_reply=None, caption_entities=None): # 发送图片
734 | """
735 | 发送图片
736 | """
737 | command = inspect.stack()[0].function
738 | if photo[:7] == "http://" or photo[:7] == "https:/":
739 | file_data = None
740 | addr = command + "?chat_id=" + str(chat_id) + "&photo=" + photo
741 | elif type(photo) == bytes:
742 | file_data = {"photo": photo}
743 | addr = command + "?chat_id=" + str(chat_id)
744 | elif type(photo) == str and '.' not in photo:
745 | file_data = None
746 | addr = command + "?chat_id=" + str(chat_id) + "&photo=" + photo
747 | else:
748 | file_data = {"photo": open(photo, 'rb')}
749 | addr = command + "?chat_id=" + str(chat_id)
750 |
751 | if caption is not None:
752 | addr += "&caption=" + quote(caption)
753 | if parse_mode in ("Markdown", "MarkdownV2", "HTML"):
754 | addr += "&parse_mode=" + parse_mode
755 | if reply_to_message_id is not None:
756 | addr += "&reply_to_message_id=" + str(reply_to_message_id)
757 | if reply_markup is not None:
758 | addr += "&reply_markup=" + json.dumps(reply_markup)
759 | if allow_sending_without_reply is not None:
760 | addr += "&allow_sending_without_reply=" + str(allow_sending_without_reply)
761 | if caption_entities is not None:
762 | addr += "&caption_entities=" + json.dumps(caption_entities)
763 |
764 | if file_data is None:
765 | return self.request.post(addr)
766 | else:
767 | return self.request.postFile(addr, file_data)
768 |
769 | def sendVideo(self, chat_id, video, caption=None, parse_mode="Text", reply_to_message_id=None,
770 | reply_markup=None, allow_sending_without_reply=None, caption_entities=None):
771 | """
772 | 发送视频
773 | """
774 | command = inspect.stack()[0].function
775 | if video[:7] == "http://" or video[:7] == "https:/":
776 | file_data = None
777 | addr = command + "?chat_id=" + str(chat_id) + "&video=" + video
778 | elif type(video) == bytes:
779 | file_data = {"video": video}
780 | addr = command + "?chat_id=" + str(chat_id)
781 | elif type(video) == str and '.' not in video:
782 | file_data = None
783 | addr = command + "?chat_id=" + str(chat_id) + "&video=" + video
784 | else:
785 | file_data = {"video": open(video, 'rb')}
786 | addr = command + "?chat_id=" + str(chat_id)
787 |
788 | if caption is not None:
789 | addr += "&caption=" + quote(caption)
790 | if parse_mode in ("Markdown", "MarkdownV2", "HTML"):
791 | addr += "&parse_mode=" + parse_mode
792 | if reply_to_message_id is not None:
793 | addr += "&reply_to_message_id=" + str(reply_to_message_id)
794 | if reply_markup is not None:
795 | addr += "&reply_markup=" + json.dumps(reply_markup)
796 | if allow_sending_without_reply is not None:
797 | addr += "&allow_sending_without_reply=" + str(allow_sending_without_reply)
798 | if caption_entities is not None:
799 | addr += "&caption_entities=" + json.dumps(caption_entities)
800 |
801 | if file_data is None:
802 | return self.request.post(addr)
803 | else:
804 | return self.request.postFile(addr, file_data)
805 |
806 | def sendVideoNote(self, chat_id, video_note, caption=None, parse_mode="Text", reply_to_message_id=None,
807 | reply_markup=None, allow_sending_without_reply=None):
808 | """
809 | 发送圆形或方形视频?
810 | """
811 | command = inspect.stack()[0].function
812 | char_id_str = str(chat_id)
813 | if video_note[:7] == "http://" or video_note[:7] == "https:/":
814 | file_data = None
815 | addr = command + "?chat_id=" + char_id_str + "&video_note=" + video_note
816 | elif type(video_note) == bytes:
817 | file_data = {"video_note": video_note}
818 | addr = command + "?chat_id=" + char_id_str
819 | elif type(video_note) == str and '.' not in video_note:
820 | file_data = None
821 | addr = command + "?chat_id=" + char_id_str + "&video_note=" + video_note
822 | else:
823 | file_data = {"video_note": open(video_note, 'rb')}
824 | addr = command + "?chat_id=" + char_id_str
825 |
826 | if caption is not None:
827 | addr += "&caption=" + quote(caption)
828 | if parse_mode in ("Markdown", "MarkdownV2", "HTML"):
829 | addr += "&parse_mode=" + parse_mode
830 | if reply_to_message_id is not None:
831 | addr += "&reply_to_message_id=" + str(reply_to_message_id)
832 | if reply_markup is not None:
833 | addr += "&reply_markup=" + json.dumps(reply_markup)
834 | if allow_sending_without_reply is not None:
835 | addr += "&allow_sending_without_reply=" + str(allow_sending_without_reply)
836 |
837 | if file_data is None:
838 | return self.request.post(addr)
839 | else:
840 | return self.request.postFile(addr, file_data)
841 |
842 | def sendMediaGroup(self, chat_id, medias, disable_notification=None, reply_to_message_id=None,
843 | reply_markup=None, allow_sending_without_reply=None): # 暂未弄懂格式。
844 | """
845 | 使用此方法可以将一组照片,视频,文档或音频作为相册发送。
846 | 文档和音频文件只能在具有相同类型消息的相册中分组。
847 | (目前只支持http链接和文件id,暂不支持上传文件)
848 | media的格式:(同时请求需要加入header头,指定传送参数为json类型,
849 | 并且将data由字典转为json字符串传送)
850 | medias ={
851 | 'caption': 'test',
852 | 'media': [
853 | {
854 | 'type': 'photo',
855 | 'media': 'https://xxxx.com/sample/7kwx_2.jpg'
856 | },
857 | {
858 | 'type': 'photo',
859 | 'media': 'AgACAgQAAx0ETbyLwwADeF5s6QosSI_IW3rKir3PrMUX'
860 | }
861 | ]
862 | }
863 | InputMediaPhoto:
864 | type
865 | media
866 | caption
867 | parse_mode
868 |
869 | InputMediaVideo:
870 | type
871 | media
872 | thumb
873 | caption
874 | parse_mode
875 | width
876 | height
877 | duration
878 | supports_streaming
879 | """
880 | command = inspect.stack()[0].function
881 | addr = command + "?chat_id=" + str(chat_id)
882 | if disable_notification is not None:
883 | addr += "&disable_notification=" + str(disable_notification)
884 | if reply_to_message_id is not None:
885 | addr += "&reply_to_message_id=" + str(reply_to_message_id)
886 | if reply_markup is not None:
887 | addr += "&reply_markup=" + json.dumps(reply_markup)
888 | if allow_sending_without_reply is not None:
889 | addr += "&allow_sending_without_reply=" + str(allow_sending_without_reply)
890 |
891 | return self.request.postJson(addr, medias)
892 |
893 | def sendDocument(self, chat_id, document, caption=None, parse_mode="Text",
894 | reply_to_message_id=None, reply_markup=None, disable_content_type_detection=None,
895 | allow_sending_without_reply=None, caption_entities=None):
896 | """
897 | 发送文件
898 | """
899 | command = inspect.stack()[0].function
900 | if document[:7] == "http://" or document[:7] == "https:/":
901 | file_data = None
902 | addr = command + "?chat_id=" + str(chat_id) + "&document=" + document
903 | elif type(document) == bytes:
904 | file_data = {"document": document}
905 | addr = command + "?chat_id=" + str(chat_id)
906 | elif type(document) == str and '.' not in document:
907 | file_data = None
908 | addr = command + "?chat_id=" + str(chat_id) + "&document=" + document
909 | else:
910 | file_data = {"document": open(document, 'rb')}
911 | addr = command + "?chat_id=" + str(chat_id)
912 |
913 | if caption is not None:
914 | addr += "&caption=" + quote(caption)
915 | if parse_mode in ("Markdown", "MarkdownV2", "HTML"):
916 | addr += "&parse_mode=" + parse_mode
917 | if reply_to_message_id is not None:
918 | addr += "&reply_to_message_id=" + str(reply_to_message_id)
919 | if reply_markup is not None:
920 | addr += "&reply_markup=" + json.dumps(reply_markup)
921 | if disable_content_type_detection is not None:
922 | addr += "&disable_content_type_detection=" + str(disable_content_type_detection)
923 | if allow_sending_without_reply is not None:
924 | addr += "&allow_sending_without_reply=" + str(allow_sending_without_reply)
925 | if caption_entities is not None:
926 | addr += "&caption_entities=" + json.dumps(caption_entities)
927 |
928 | if file_data is None:
929 | return self.request.post(addr)
930 | else:
931 | return self.request.postFile(addr, file_data)
932 |
933 | def leaveChat(self, chat_id):
934 | """
935 | 退出群组
936 | """
937 | command = inspect.stack()[0].function
938 | addr = command + "?chat_id=" + str(chat_id)
939 | return self.request.post(addr)
940 |
941 | def getChat(self, chat_id):
942 | """
943 | 使用此方法可获取有关聊天的最新信息(一对一对话的用户的当前名称,
944 | 用户的当前用户名,组或频道等)。
945 | 成功返回一个Chat对象。
946 | """
947 | command = inspect.stack()[0].function
948 | addr = command + "?chat_id=" + str(chat_id)
949 | return self.request.post(addr)
950 |
951 | def getChatAdministrators(self, chat_id):
952 | """
953 | 获取群组所有管理员信息
954 | """
955 | command = inspect.stack()[0].function
956 | addr = command + "?chat_id=" + str(chat_id)
957 | return self.request.post(addr)
958 |
959 | def getChatMembersCount(self, chat_id):
960 | """
961 | 获取群组成员总数
962 | """
963 | command = inspect.stack()[0].function
964 | addr = command + "?chat_id=" + str(chat_id)
965 | return self.request.post(addr)
966 |
967 | def getUserProfilePhotos(self, user_id, offset=None, limit=None):
968 | """
969 | 获取用户头像
970 | """
971 | command = inspect.stack()[0].function
972 | addr = command + "?user_id=" + str(user_id)
973 |
974 | if offset is not None:
975 | addr += "&offset=" + str(offset)
976 | if limit is not None and limit in list(range(1, 101)):
977 | addr += "&limit=" + str(limit)
978 | return self.request.post(addr)
979 |
980 | def getChatMember(self, chat_id, user_id):
981 | """
982 | 获取群组特定用户信息
983 | """
984 | command = inspect.stack()[0].function
985 | addr = command + "?chat_id=" + str(chat_id) + "&user_id=" + str(user_id)
986 | return self.request.post(addr)
987 |
988 | def setChatTitle(self, chat_id, title):
989 | """
990 | 设置群组标题
991 | """
992 | command = inspect.stack()[0].function
993 | addr = command + "?chat_id=" + str(chat_id) + "&title=" + quote(str(title))
994 | return self.request.post(addr)
995 |
996 | def setChatDescription(self, chat_id, description):
997 | """
998 | 设置群组简介(测试好像无效。。)
999 | //FIXME
1000 | """
1001 | command = inspect.stack()[0].function
1002 | addr = command + "?chat_id=" + str(chat_id) + "&description=" + quote(str(description))
1003 | return self.request.post(addr)
1004 |
1005 | def setChatPhoto(self, chat_id, photo):
1006 | """
1007 | 设置群组头像
1008 | """
1009 | command = inspect.stack()[0].function
1010 | file_data = {"photo": open(photo, 'rb')}
1011 | addr = command + "?chat_id=" + str(chat_id)
1012 |
1013 | return self.request.postFile(addr, file_data)
1014 |
1015 | def deleteChatPhoto(self, chat_id):
1016 | """
1017 | 删除群组头像
1018 | """
1019 | command = inspect.stack()[0].function
1020 | addr = command + "?chat_id=" + str(chat_id)
1021 | return self.request.post(addr)
1022 |
1023 | def setChatPermissions(self, chat_id, permissions):
1024 | """
1025 | 设置群组默认聊天权限
1026 | permissions = {
1027 | 'can_send_messages':False,
1028 | 'can_send_media_messages':False,
1029 | 'can_send_polls':False,
1030 | 'can_send_other_messages':False,
1031 | 'can_add_web_page_previews':False,
1032 | 'can_change_info':False,
1033 | 'can_invite_users':False,
1034 | 'can_pin_messages':False
1035 | }
1036 | """
1037 | command = inspect.stack()[0].function
1038 | addr = command + "?chat_id=" + str(chat_id)
1039 | permissions = {"permissions": permissions}
1040 |
1041 | return self.request.postJson(addr, permissions)
1042 |
1043 | def restrictChatMember(self, chat_id, user_id, permissions, until_date=None):
1044 | """
1045 | 限制群组用户权限
1046 | permissions = {
1047 | 'can_send_messages':False,
1048 | 'can_send_media_messages':False,
1049 | 'can_send_polls':False,
1050 | 'can_send_other_messages':False,
1051 | 'can_add_web_page_previews':False,
1052 | 'can_change_info':False,
1053 | 'can_invite_users':False,
1054 | 'can_pin_messages':False
1055 | }
1056 | until_date format:
1057 | timestamp + offset
1058 | """
1059 | command = inspect.stack()[0].function
1060 | addr = command + "?chat_id=" + \
1061 | str(chat_id) + "&user_id=" + str(user_id)
1062 | if len(permissions) != 8:
1063 | return False
1064 | if until_date is not None:
1065 | until_date = int(time.time()) + int(until_date)
1066 | addr += "&until_date=" + str(until_date)
1067 |
1068 | return self.request.postJson(addr, permissions)
1069 |
1070 | def promoteChatMember(self, chat_id, user_id, is_anonymous=None,
1071 | can_change_info=None, can_post_messages=None, can_edit_messages=None,
1072 | can_delete_messages=None, can_invite_users=None, can_restrict_members=None,
1073 | can_pin_messages=None, can_promote_members=None):
1074 | """
1075 | 修改管理员权限(只能修改由机器人任命的管理员的权限,
1076 | 范围为机器人权限的子集)
1077 | {
1078 | 'is_anonymous':None,
1079 | 'can_change_info':False,
1080 | 'can_post_messages':False,
1081 | 'can_edit_messages':False,
1082 | 'can_delete_messages':False,
1083 | 'can_invite_users':False,
1084 | 'can_restrict_members':False,
1085 | 'can_pin_messages':False,
1086 | 'can_promote_members':False
1087 | }
1088 | """
1089 | command = inspect.stack()[0].function
1090 |
1091 | addr = command + "?chat_id=" + str(chat_id) + "&user_id=" + str(user_id)
1092 |
1093 | if is_anonymous is not None:
1094 | addr += "&is_anonymous=" + str(is_anonymous)
1095 | if can_change_info is not None:
1096 | addr += "&can_change_info=" + str(can_change_info)
1097 | if can_post_messages is not None:
1098 | addr += "&can_post_messages=" + str(can_post_messages)
1099 | if can_edit_messages is not None:
1100 | addr += "&can_edit_messages=" + str(can_edit_messages)
1101 | if can_delete_messages is not None:
1102 | addr += "&can_delete_messages=" + str(can_delete_messages)
1103 | if can_invite_users is not None:
1104 | addr += "&can_invite_users=" + str(can_invite_users)
1105 | if can_restrict_members is not None:
1106 | addr += "&can_restrict_members=" + str(can_restrict_members)
1107 | if can_pin_messages is not None:
1108 | addr += "&can_pin_messages=" + str(can_pin_messages)
1109 | if can_promote_members is not None:
1110 | addr += "&can_promote_members=" + str(can_promote_members)
1111 |
1112 | return self.request.post(addr)
1113 |
1114 | def pinChatMessage(self, chat_id, message_id, disable_notification=None):
1115 | """
1116 | 置顶消息
1117 | """
1118 | command = inspect.stack()[0].function
1119 | addr = command + "?chat_id=" + str(chat_id) + "&message_id=" + str(message_id)
1120 | if disable_notification is not None:
1121 | addr += "&disable_notification=" + str(disable_notification)
1122 |
1123 | return self.request.post(addr)
1124 |
1125 | def unpinChatMessage(self, chat_id, message_id=None):
1126 | """
1127 | 使用此方法可以从聊天中的置顶消息列表中删除消息
1128 | """
1129 | command = inspect.stack()[0].function
1130 | addr = command + "?chat_id=" + str(chat_id)
1131 |
1132 | if message_id is not None:
1133 | addr += "&message_id=" + str(message_id)
1134 |
1135 | return self.request.post(addr)
1136 |
1137 | def unpinAllChatMessages(self, chat_id):
1138 | """
1139 | 使用此方法可以清除聊天中的置顶消息列表中的所有置顶消息
1140 | """
1141 | command = inspect.stack()[0].function
1142 | addr = command + "?chat_id=" + str(chat_id)
1143 |
1144 | return self.request.post(addr)
1145 |
1146 | def sendLocation(self, chat_id, latitude, longitude,
1147 | horizontal_accuracy=None, live_period=None,
1148 | heading=None, disable_notification=None,
1149 | reply_to_message_id=None, reply_markup=None,
1150 | allow_sending_without_reply=None):
1151 | """
1152 | 发送地图定位,经纬度
1153 | """
1154 | command = inspect.stack()[0].function
1155 | addr = command + "?chat_id=" + str(chat_id) + "&latitude=" + str(
1156 | float(latitude)) + "&longitude=" + str(float(longitude))
1157 | if live_period is not None:
1158 | addr += "&live_period=" + str(live_period)
1159 | if horizontal_accuracy is not None:
1160 | addr += "&horizontal_accuracy=" + str(horizontal_accuracy)
1161 | if heading is not None:
1162 | addr += "&heading=" + str(heading)
1163 | if disable_notification is not None:
1164 | addr += "&disable_notification=" + str(disable_notification)
1165 | if reply_to_message_id is not None:
1166 | addr += "&reply_to_message_id=" + str(reply_to_message_id)
1167 | if reply_markup is not None:
1168 | addr += "&reply_markup=" + json.dumps(reply_markup)
1169 | if allow_sending_without_reply is not None:
1170 | addr += "&allow_sending_without_reply=" + str(allow_sending_without_reply)
1171 |
1172 | return self.request.post(addr)
1173 |
1174 | def sendContact(self, chat_id, phone_number, first_name, last_name=None, reply_to_message_id=None,
1175 | reply_markup=None, allow_sending_without_reply=None):
1176 | """
1177 | 发送联系人信息
1178 | """
1179 | command = inspect.stack()[0].function
1180 | addr = command + "?chat_id=" + str(chat_id) + "&phone_number=" + str(phone_number) + "&first_name=" + str(
1181 | first_name)
1182 | if last_name is not None:
1183 | addr += "&last_name=" + str(last_name)
1184 | if reply_to_message_id is not None:
1185 | addr += "&reply_to_message_id=" + str(reply_to_message_id)
1186 | if reply_markup is not None:
1187 | addr += "&reply_markup=" + json.dumps(reply_markup)
1188 | if allow_sending_without_reply is not None:
1189 | addr += "&allow_sending_without_reply=" + str(allow_sending_without_reply)
1190 |
1191 | return self.request.post(addr)
1192 |
1193 | def sendPoll(self, chat_id, question, options, is_anonymous=None,
1194 | type_=None, allows_multiple_answers=None, correct_option_id=None,
1195 | explanation=None, explanation_parse_mode=None, explanation_entities=None,
1196 | open_period=None, close_date=None, is_closed=None, disable_notification=None,
1197 | reply_to_message_id=None, allow_sending_without_reply=None, reply_markup=None):
1198 | """
1199 | 使用此方法发起投票(quiz or regular, defaults to regular)
1200 | options格式:
1201 | options = [
1202 | "option 1",
1203 | "option 2"
1204 | ]
1205 | """
1206 | command = inspect.stack()[0].function
1207 | addr = command + "?chat_id=" + str(chat_id) + "&question=" + str(question)
1208 | addr += "&options=" + json.dumps(options)
1209 |
1210 | if is_anonymous is not None:
1211 | addr += "&is_anonymous=" + str(is_anonymous)
1212 | if type_ is not None:
1213 | addr += "&type=" + str(type_)
1214 |
1215 | if type_ == "quiz":
1216 | if allows_multiple_answers is not None:
1217 | addr += "&allows_multiple_answers=" + str(allows_multiple_answers)
1218 | if correct_option_id is not None:
1219 | addr += "&correct_option_id=" + str(correct_option_id)
1220 | if explanation is not None:
1221 | addr += "&explanation=" + str(explanation)
1222 | if explanation_parse_mode is not None:
1223 | addr += "&explanation_parse_mode=" + str(explanation_parse_mode)
1224 | if explanation_entities is not None:
1225 | addr += "&explanation_entities=" + json.dumps(explanation_entities)
1226 |
1227 | if open_period is not None:
1228 | addr += "&open_period=" + str(open_period)
1229 | if close_date is not None:
1230 | addr += "&close_date=" + str(close_date)
1231 | if is_closed is not None:
1232 | addr += "&is_closed=" + str(is_closed)
1233 | if disable_notification is not None:
1234 | addr += "&disable_notification=" + str(disable_notification)
1235 | if reply_to_message_id is not None:
1236 | addr += "&reply_to_message_id=" + str(reply_to_message_id)
1237 | if allow_sending_without_reply is not None:
1238 | addr += "&allow_sending_without_reply=" + str(allow_sending_without_reply)
1239 | if reply_markup is not None:
1240 | addr += "&reply_markup=" + json.dumps(reply_markup)
1241 |
1242 | return self.request.post(addr)
1243 |
1244 | def sendDice(self, chat_id, emoji, disable_notification=None,
1245 | reply_to_message_id=None, allow_sending_without_reply=None,
1246 | reply_markup=None):
1247 | """
1248 | 使用此方法发送一个动画表情
1249 | emoji参数必须是以下几种:
1250 | 1.dice(骰子) values 1-6
1251 | 2.darts(飞镖) values 1-6
1252 | 3.basketball(篮球) values 1-5
1253 | 4.football(足球) values 1-5
1254 | 5.slot machine(老虎机) values 1-64
1255 | 默认为骰子
1256 | """
1257 | command = inspect.stack()[0].function
1258 | addr = command + "?chat_id=" + str(chat_id) + "&emoji=" + str(emoji)
1259 |
1260 | if disable_notification is not None:
1261 | addr += "&disable_notification=" + str(disable_notification)
1262 | if reply_to_message_id is not None:
1263 | addr += "&reply_to_message_id=" + str(reply_to_message_id)
1264 | if allow_sending_without_reply is not None:
1265 | addr += "&allow_sending_without_reply=" + str(allow_sending_without_reply)
1266 | if reply_markup is not None:
1267 | addr += "&reply_markup=" + json.dumps(reply_markup)
1268 |
1269 | return self.request.post(addr)
1270 |
1271 | def sendVenue(self, chat_id, latitude, longitude, title, address,
1272 | allow_sending_without_reply=None,
1273 | foursquare_id=None, foursquare_type=None,
1274 | google_place_id=None, google_place_type=None,
1275 | disable_notification=None, reply_to_message_id=None,
1276 | reply_markup=None):
1277 | """
1278 | 使用此方法发送关于地点的信息。
1279 | (发送地点,显示在地图上)
1280 | """
1281 | command = inspect.stack()[0].function
1282 | addr = command + "?chat_id=" + str(chat_id) + "&latitude=" + str(float(latitude)) + "&longitude=" + str(
1283 | float(longitude)) + "&title=" + str(title) + "&address=" + str(address)
1284 | if allow_sending_without_reply is not None:
1285 | addr += "&allow_sending_without_reply=" + str(allow_sending_without_reply)
1286 | if foursquare_id is not None:
1287 | addr += "&foursquare_id=" + str(foursquare_id)
1288 | if foursquare_type is not None:
1289 | addr += "&foursquare_type=" + str(foursquare_type)
1290 | if google_place_id is not None:
1291 | addr += "&google_place_id=" + str(google_place_id)
1292 | if google_place_type is not None:
1293 | addr += "&google_place_type=" + str(google_place_type)
1294 | if disable_notification is not None:
1295 | addr += "&disable_notification=" + str(disable_notification)
1296 | if reply_to_message_id is not None:
1297 | addr += "&reply_to_message_id=" + str(reply_to_message_id)
1298 | if reply_markup is not None:
1299 | addr += "&reply_markup=" + json.dumps(reply_markup)
1300 |
1301 |
1302 | return self.request.post(addr)
1303 |
1304 | def sendChatAction(self, chat_id, action):
1305 | """
1306 | 发送聊天状态,类似: 正在输入...
1307 | typing :for text messages,
1308 | upload_photo :for photos,
1309 | record_video/upload_video :for videos,
1310 | record_audio/upload_audio :for audio files,
1311 | upload_document :for general files,
1312 | find_location :for location data,
1313 | record_video_note/upload_video_note :for video notes.
1314 | """
1315 | command = inspect.stack()[0].function
1316 | addr = command + "?chat_id=" + str(chat_id) + "&action=" + str(action)
1317 | return self.request.post(addr)
1318 |
1319 | def forwardMessage(self, chat_id, from_chat_id, message_id, disable_notification=None):
1320 | """
1321 | 转发消息
1322 | """
1323 | command = inspect.stack()[0].function
1324 | addr = command + "?chat_id=" + str(chat_id) + "&from_chat_id=" + str(from_chat_id) \
1325 | + "&message_id=" + str(message_id)
1326 |
1327 | if disable_notification is not None:
1328 | addr += "&disable_notification=" + str(disable_notification)
1329 |
1330 | return self.request.post(addr)
1331 |
1332 | def copyMessage(self, chat_id, from_chat_id, message_id,
1333 | caption=None, parse_mode="Text", caption_entities=None,
1334 | disable_notification=None, reply_to_message_id=None,
1335 | allow_sending_without_reply=None, reply_markup=None):
1336 | """
1337 | 使用此方法可以复制任何类型的消息。
1338 | 该方法类似于forwardMessages方法,
1339 | 但是复制的消息没有指向原始消息的链接。
1340 | """
1341 | command = inspect.stack()[0].function
1342 | addr = command + "?chat_id=" + str(chat_id) + "&from_chat_id=" + str(from_chat_id) \
1343 | + "&message_id=" + str(message_id)
1344 |
1345 | if caption is not None:
1346 | addr += "&caption=" + quote(caption)
1347 | if parse_mode in ("Markdown", "MarkdownV2", "HTML"):
1348 | addr += "&parse_mode" + parse_mode
1349 | if disable_notification is not None:
1350 | addr += "&disable_notification=" + str(disable_notification)
1351 | if reply_to_message_id is not None:
1352 | addr += "&reply_to_message_id=" + str(reply_to_message_id)
1353 | if allow_sending_without_reply is not None:
1354 | addr += "&allow_sending_without_reply=" + str(allow_sending_without_reply)
1355 | if reply_markup is not None:
1356 | addr += "&reply_markup=" + json.dumps(reply_markup)
1357 |
1358 | if caption_entities is not None:
1359 | return self.request.postJson(addr, caption_entities)
1360 | else:
1361 | return self.request.post(addr)
1362 |
1363 |
1364 | def kickChatMember(self, chat_id, user_id, until_date=None):
1365 | """
1366 | 从Group、Supergroup或者Channel中踢人,被踢者在until_date期限内不可再次加入
1367 | until_date format:
1368 | timestamp + offset
1369 | """
1370 |
1371 | command = inspect.stack()[0].function
1372 | if until_date is not None:
1373 | until_date = int(time.time()) + int(until_date)
1374 | addr = command + "?chat_id=" + str(chat_id) + "&user_id=" + str(user_id) + "&until_date=" + str(until_date)
1375 | if until_date is None:
1376 | addr = command + "?chat_id=" + \
1377 | str(chat_id) + "&user_id=" + str(user_id)
1378 |
1379 | return self.request.post(addr)
1380 |
1381 | def unbanChatMember(self, chat_id, user_id, only_if_banned=None):
1382 | """
1383 | 使用此方法可以取消超级组或频道中以前被踢过的用户的权限。
1384 | (解除user被设置的until_date)
1385 | ChatPermissions:
1386 | can_send_messages
1387 | can_send_media_messages
1388 | can_send_polls
1389 | can_send_other_messages
1390 | can_add_web_page_previews
1391 | can_change_info
1392 | can_invite_users
1393 | can_pin_messages
1394 | """
1395 |
1396 | command = inspect.stack()[0].function
1397 | addr = command + "?chat_id=" + \
1398 | str(chat_id) + "&user_id=" + str(user_id)
1399 |
1400 | if only_if_banned is not None:
1401 | addr += "&only_if_banned=" + str(only_if_banned)
1402 |
1403 | return self.request.post(addr)
1404 |
1405 | def setChatAdministratorCustomTitle(self, chat_id, user_id, custom_title):
1406 | """
1407 | 为群组的管理员设置自定义头衔
1408 | """
1409 | command = inspect.stack()[0].function
1410 | addr = command + "?chat_id=" + str(chat_id) + "&user_id=" + str(user_id) + "&custom_title=" + quote(str(custom_title))
1411 |
1412 | return self.request.post(addr)
1413 |
1414 | def exportChatInviteLink(self, chat_id):
1415 | """
1416 | 使用此方法生成新的群组分享链接,旧有分享链接全部失效,成功返回分享链接
1417 | """
1418 | command = inspect.stack()[0].function
1419 | addr = command + "?chat_id=" + str(chat_id)
1420 |
1421 | return self.request.post(addr)
1422 |
1423 | def setChatStickerSet(self, chat_id, sticker_set_name):
1424 | """
1425 | 为一个超级群组设置贴纸集
1426 | """
1427 | command = inspect.stack()[0].function
1428 | addr = command + "?chat_id=" + str(chat_id) + "&sticker_set_name=" + str(sticker_set_name)
1429 |
1430 | return self.request.post(addr)
1431 |
1432 | def addStickerToSet(self, user_id, name, emojis,
1433 | png_sticker=None, tgs_sticker=None, mask_position=None):
1434 | """
1435 | 使用此方法在机器人创建的集合中添加一个新贴纸。
1436 | 必须使用png标签或tgs标签中的一个字段。
1437 | 动画贴纸只能添加到动画贴纸组中。
1438 | 动画贴纸组最多可以有50个贴纸。
1439 | 静态贴纸组最多可以有120个贴纸。
1440 | """
1441 | command = inspect.stack()[0].function
1442 | addr = command + "?user_id=" + str(user_id) + "&name=" + str(name) \
1443 | + "&emoji=" + str(emoji)
1444 |
1445 | if png_sticker is not None and tgs_sticker is not None:
1446 | return False
1447 | elif png_sticker is None and tgs_sticker is None:
1448 | return False
1449 | else:
1450 | if png_sticker is not None:
1451 | if png_sticker[:7] == "http://" or png_sticker[:7] == "https:/":
1452 | file_data = None
1453 | addr = command + "?chat_id=" + str(chat_id) + "&png_sticker=" + png_sticker
1454 | elif type(png_sticker) == bytes:
1455 | file_data = {"png_sticker": png_sticker}
1456 | addr = command + "?chat_id=" + str(chat_id)
1457 | elif type(png_sticker) == str and '.' not in png_sticker:
1458 | file_data = None
1459 | addr = command + "?chat_id=" + str(chat_id) + "&png_sticker=" + png_sticker
1460 | else:
1461 | file_data = {"png_sticker": open(png_sticker, 'rb')}
1462 | addr = command + "?chat_id=" + str(chat_id)
1463 | elif tgs_sticker is not None:
1464 | if tgs_sticker[:7] == "http://" or tgs_sticker[:7] == "https:/":
1465 | file_data = None
1466 | addr = command + "?chat_id=" + str(chat_id) + "&tgs_sticker=" + tgs_sticker
1467 | elif type(png_sticker) == bytes:
1468 | file_data = {"tgs_sticker": tgs_sticker}
1469 | addr = command + "?chat_id=" + str(chat_id)
1470 | elif type(tgs_sticker) == str and '.' not in tgs_sticker:
1471 | file_data = None
1472 | addr = command + "?chat_id=" + str(chat_id) + "&tgs_sticker=" + tgs_sticker
1473 | else:
1474 | file_data = {"tgs_sticker": open(tgs_sticker, 'rb')}
1475 | addr = command + "?chat_id=" + str(chat_id)
1476 |
1477 | if file_data is None:
1478 | return self.request.post(addr)
1479 | else:
1480 | return self.request.postFile(addr, file_data)
1481 |
1482 | def deleteChatStickerSet(self, chat_id):
1483 | """
1484 | 删除超级群组的贴纸集
1485 | """
1486 | command = inspect.stack()[0].function
1487 | addr = command + "?chat_id=" + str(chat_id)
1488 |
1489 | return self.request.post(addr)
1490 |
1491 | def editMessageLiveLocation(self, latitude, longitude,
1492 | horizontal_accuracy=None, chat_id=None, message_id=None,
1493 | heading=None, inline_message_id=None, reply_markup=None):
1494 | """
1495 | 使用此方法编辑实时位置消息
1496 | 在未指定inline_message_id的时候chat_id和message_id为必须存在的参数
1497 | """
1498 | command = inspect.stack()[0].function
1499 |
1500 | if inline_message_id is None:
1501 | if message_id is None or chat_id is None:
1502 | return False
1503 |
1504 | if inline_message_id is not None:
1505 | addr = command + "?inline_message_id=" + str(inline_message_id)
1506 | else:
1507 | addr = command + "?chat_id=" + str(chat_id)
1508 | addr += "&message_id=" + str(message_id)
1509 |
1510 | addr += "&latitude=" + str(latitude)
1511 | addr += "&longitude=" + str(longitude)
1512 |
1513 | if horizontal_accuracy is not None:
1514 | addr += "&horizontal_accuracy=" + str(horizontal_accuracy)
1515 | if heading is not None:
1516 | addr += "&heading=" + str(heading)
1517 | if reply_markup is not None:
1518 | addr += "&reply_markup=" + json.dumps(reply_markup)
1519 |
1520 | return self.request.post(addr)
1521 |
1522 | def stopMessageLiveLocation(self, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
1523 | """
1524 | 使用此方法可在活动期间到期前停止更新活动位置消息
1525 | 在未指定inline_message_id的时候chat_id和message_id为必须存在的参数
1526 | """
1527 | command = inspect.stack()[0].function
1528 |
1529 | if inline_message_id is None:
1530 | if message_id is None or chat_id is None:
1531 | return False
1532 |
1533 | if inline_message_id is not None:
1534 | addr = command + "?inline_message_id=" + str(inline_message_id)
1535 | else:
1536 | addr = command + "?chat_id=" + str(chat_id)
1537 | addr += "&message_id=" + str(message_id)
1538 |
1539 | if reply_markup is not None:
1540 | addr += "&reply_markup=" + json.dumps(reply_markup)
1541 |
1542 | return self.request.post(addr)
1543 |
1544 | def setMyCommands(self, commands):
1545 | """
1546 | 使用此方法更改机器人的命令列表
1547 | commands传入格式示例:
1548 | commands = [
1549 | {"command": "start", "description": "插件列表"},
1550 | {"command": "bing", "description": "获取每日Bing壁纸"}
1551 | ]
1552 | """
1553 | command = inspect.stack()[0].function
1554 | addr = command
1555 | commands = {"commands": commands}
1556 |
1557 | return self.request.postJson(addr, commands)
1558 |
1559 | def getMyCommands(self):
1560 | """
1561 | 使用此方法获取机器人当前的命令列表
1562 | """
1563 | command = inspect.stack()[0].function
1564 | addr = command
1565 |
1566 | return self.request.post(addr)
1567 |
1568 | # Updating messages
1569 | def editMessageText(self, text, chat_id=None, message_id=None, inline_message_id=None,
1570 | parse_mode="Text", disable_web_page_preview=None,
1571 | reply_markup=None, entities=None):
1572 | """
1573 | 编辑一条文本消息.成功时,若消息为Bot发送则返回编辑后的消息,其他返回True
1574 | 在未指定inline_message_id的时候chat_id和message_id为必须存在的参数
1575 | """
1576 | command = inspect.stack()[0].function
1577 |
1578 | if inline_message_id is None:
1579 | if message_id is None or chat_id is None:
1580 | return False
1581 |
1582 | if inline_message_id is not None:
1583 | addr = command + "?inline_message_id=" + str(inline_message_id)
1584 | else:
1585 | addr = command + "?chat_id=" + str(chat_id)
1586 | addr += "&message_id=" + str(message_id)
1587 |
1588 | addr += "&text=" + quote(str(text))
1589 | if parse_mode in ("Markdown", "MarkdownV2", "HTML"):
1590 | addr += "&parse_mode=" + str(parse_mode)
1591 | if disable_web_page_preview is not None:
1592 | addr += "&disable_web_page_preview=" + \
1593 | str(disable_web_page_preview)
1594 | if reply_markup is not None:
1595 | addr += "&reply_markup=" + json.dumps(reply_markup)
1596 | if entities is not None:
1597 | addr += "&entities=" + json.dumps(entities)
1598 |
1599 | return self.request.post(addr)
1600 |
1601 | def editMessageCaption(self, chat_id=None, message_id=None,
1602 | inline_message_id=None, caption=None, parse_mode="Text",
1603 | reply_markup=None, caption_entities=None):
1604 | """
1605 | 编辑消息的Caption。成功时,若消息为Bot发送则返回编辑后的消息,其他返回True
1606 | 在未指定inline_message_id的时候chat_id和message_id为必须存在的参数
1607 | """
1608 | command = inspect.stack()[0].function
1609 | if inline_message_id is None:
1610 | if message_id is None or chat_id is None:
1611 | return False
1612 |
1613 | if inline_message_id is not None:
1614 | addr = command + "?inline_message_id=" + str(inline_message_id)
1615 | else:
1616 | addr = command + "?chat_id=" + str(chat_id)
1617 | addr += "&message_id=" + str(message_id)
1618 |
1619 | if caption is not None:
1620 | addr += "&caption=" + quote(str(caption))
1621 | if parse_mode in ("Markdown", "MarkdownV2", "HTML"):
1622 | addr += "&parse_mode=" + str(parse_mode)
1623 | if reply_markup is not None:
1624 | addr += "&reply_markup=" + str(reply_markup)
1625 | if caption_entities is not None:
1626 | addr += "&caption_entities=" + json.dumps(caption_entities)
1627 |
1628 | return self.request.post(addr)
1629 |
1630 | def editMessageMedia(self, media, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
1631 | """
1632 | 编辑消息媒体
1633 | 在未指定inline_message_id的时候chat_id和message_id为必须存在的参数
1634 | media format:
1635 | media = {
1636 | 'media':{
1637 | 'type': 'photo',
1638 | 'media': 'http://pic1.win4000.com/pic/d/6a/25a2c0e959.jpg',
1639 | 'caption': '编辑后的Media'
1640 | }
1641 | }
1642 | """
1643 | command = inspect.stack()[0].function
1644 | if inline_message_id is None:
1645 | if message_id is None or chat_id is None:
1646 | return False
1647 |
1648 | if inline_message_id is not None:
1649 | addr = command + "?inline_message_id=" + str(inline_message_id)
1650 | else:
1651 | addr = command + "?chat_id=" + str(chat_id)
1652 | addr += "&message_id=" + str(message_id)
1653 |
1654 | if reply_markup is not None:
1655 | addr += "&reply_markup=" + json.dumps(reply_markup)
1656 |
1657 | return self.request.postJson(addr, media)
1658 |
1659 | def editMessageReplyMarkup(self, chat_id=None, message_id=None, inline_message_id=None, reply_markup=None):
1660 | """
1661 | 编辑MessageReplyMarkup
1662 | 在未指定inline_message_id的时候chat_id和message_id为必须存在的参数
1663 | """
1664 | command = inspect.stack()[0].function
1665 | if inline_message_id is None:
1666 | if message_id is None or chat_id is None:
1667 | return False
1668 |
1669 | if inline_message_id is not None:
1670 | addr = command + "?inline_message_id=" + str(inline_message_id)
1671 | else:
1672 | addr = command + "?chat_id=" + str(chat_id)
1673 | addr += "&message_id=" + str(message_id)
1674 |
1675 | if reply_markup is not None:
1676 | addr += "&reply_markup=" + json.dumps(reply_markup)
1677 |
1678 | return self.request.post(addr)
1679 |
1680 | def stopPoll(self, chat_id, message_id, reply_markup=None):
1681 | """
1682 | 停止投票?并返回最终结果
1683 | """
1684 | command = inspect.stack()[0].function
1685 | addr = command + "?chat_id=" + str(chat_id) + "&message_id=" + str(message_id)
1686 |
1687 | if reply_markup is not None:
1688 | addr += "&reply_markup=" + json.dumps(reply_markup)
1689 |
1690 | return self.request.post(addr)
1691 |
1692 | def deleteMessage(self, chat_id, message_id):
1693 | """
1694 | 删除一条消息,机器人必须具备恰当的权限
1695 | """
1696 | command = inspect.stack()[0].function
1697 | addr = command + "?chat_id=" + str(chat_id) + "&message_id=" + str(message_id)
1698 |
1699 | return self.request.post(addr)
1700 |
1701 | # Inline mode
1702 |
1703 | def answerInlineQuery(self, inline_query_id, results, cache_time=None,
1704 | is_personal=None, next_offset=None, switch_pm_text=None, switch_pm_parameter=None):
1705 | """
1706 | 使用此方法发送Inline mode的应答
1707 | """
1708 | command = inspect.stack()[0].function
1709 | addr = command + "?inline_query_id=" + str(inline_query_id)
1710 | if cache_time is not None:
1711 | addr += "&cache_time=" + str(cache_time)
1712 | if is_personal is not None:
1713 | addr += "&is_personal=" + str(is_personal)
1714 | if next_offset is not None:
1715 | addr += "&next_offset=" + str(next_offset)
1716 | if switch_pm_text is not None:
1717 | addr += "&switch_pm_text=" + str(switch_pm_text)
1718 | if switch_pm_parameter is not None:
1719 | addr += "&switch_pm_parameter=" + str(switch_pm_parameter)
1720 |
1721 | return self.request.postJson(addr, results)
1722 |
1723 | def answerCallbackQuery(self, callback_query_id, text=None, show_alert="false", url=None, cache_time=0):
1724 | """
1725 | 使用此方法发送CallbackQuery的应答
1726 | InlineKeyboardMarkup格式:
1727 | replyKeyboard = [
1728 | [
1729 | { "text": "命令菜单","callback_data":"/start"},
1730 | { "text": "一排之二","url":"https://google.com"}
1731 | ],
1732 | [
1733 | { "text": "二排之一","url":"https://google.com"},
1734 | { "text": "二排之二","url":"https://google.com"},
1735 | { "text": "二排之三","url":"https://google.com"}
1736 | ]
1737 | ]
1738 | reply_markup = {
1739 | "inline_keyboard": replyKeyboard
1740 | }
1741 | ReplyKeyboardMarkup格式(似乎不能用于群组):
1742 | replyKeyboard = [
1743 | [
1744 | { "text": "命令菜单"},
1745 | { "text": "一排之二"}
1746 | ],
1747 | [
1748 | { "text": "二排之一"},
1749 | { "text": "二排之二"},
1750 | { "text": "二排之三"}
1751 | ]
1752 | ]
1753 | reply_markup = {
1754 | "keyboard": replyKeyboard,
1755 | "resize_keyboard": bool("false"),
1756 | "one_time_keyboard": bool("false"),
1757 | "selective": bool("true")
1758 | }
1759 | ReplyKeyboardRemove格式:
1760 | reply_markup = {
1761 | "remove_keyboard": bool("true"),
1762 | "selective": bool("true")
1763 | }
1764 | """
1765 | command = inspect.stack()[0].function
1766 | addr = command + "?callback_query_id=" + str(callback_query_id)
1767 | if text is not None:
1768 | addr += "&text=" + quote(str(text))
1769 | if show_alert == "true":
1770 | addr += "&show_alert=" + str(bool(show_alert))
1771 | if url is not None:
1772 | addr += "&url=" + str(url)
1773 | if cache_time != 0:
1774 | addr += "&cache_time=" + str(cache_time)
1775 |
1776 | return self.request.post(addr)
1777 |
1778 | # Stickers
1779 | def sendSticker(self, chat_id, sticker, disable_notification=None,
1780 | reply_to_message_id=None, reply_markup=None,
1781 | allow_sending_without_reply=None):
1782 | """
1783 | 使用此方法发送静态、webp或动画、tgs贴纸
1784 | """
1785 | command = inspect.stack()[0].function
1786 |
1787 | if sticker[:7] == "http://" or sticker[:7] == "https:/":
1788 | file_data = None
1789 | addr = command + "?chat_id=" + str(chat_id) + "&sticker=" + sticker
1790 | elif type(sticker) == bytes:
1791 | file_data = {"sticker": sticker}
1792 | addr = command + "?chat_id=" + str(chat_id)
1793 | elif type(sticker) == str and '.' not in sticker:
1794 | file_data = None
1795 | addr = command + "?chat_id=" + str(chat_id) + "&sticker=" + sticker
1796 | else:
1797 | file_data = {"sticker": open(sticker, 'rb')}
1798 | addr = command + "?chat_id=" + str(chat_id)
1799 |
1800 | if disable_notification is not None:
1801 | addr += "&disable_notification=" + str(disable_notification)
1802 | if reply_to_message_id is not None:
1803 | addr += "&reply_to_message_id=" + str(reply_to_message_id)
1804 | if reply_markup is not None:
1805 | addr += "&reply_markup=" + json.dumps(reply_markup)
1806 | if allow_sending_without_reply is not None:
1807 | addr += "&allow_sending_without_reply=" + str(allow_sending_without_reply)
1808 |
1809 | if file_data is None:
1810 | return self.request.post(addr)
1811 | else:
1812 | return self.request.postFile(addr, file_data)
1813 |
1814 | def getStickerSet(self, name):
1815 | """
1816 | 使用此方法获取贴纸集
1817 | """
1818 | command = inspect.stack()[0].function
1819 | addr = command + "?name=" + str(name)
1820 |
1821 | return self.request.post(addr)
1822 |
1823 | def uploadStickerFile(self, user_id, name, title, emojis,
1824 | png_sticker=None, tgs_sticker=None, contains_masks=None,
1825 | mask_position=None):
1826 | """
1827 | 使用此方法可以上传带有标签的.PNG文件
1828 | 以供以后在createNewStickerSet和addStickerToSet方法中使用
1829 | (可以多次使用)
1830 | """
1831 | command = inspect.stack()[0].function
1832 |
1833 | user_id_str = str(user_id)
1834 | if png_sticker[:7] == "http://" or png_sticker[:7] == "https:/":
1835 | file_data = None
1836 | addr = command + "?user_id=" + user_id_str + "&png_sticker=" + png_sticker
1837 | elif type(png_sticker) == bytes:
1838 | file_data = {"png_sticker": png_sticker}
1839 | addr = command + "?user_id=" + user_id_str
1840 | elif type(png_sticker) == str and '.' not in png_sticker:
1841 | file_data = None
1842 | addr = command + "?user_id=" + user_id_str + "&png_sticker=" + png_sticker
1843 | else:
1844 | file_data = {"png_sticker": open(png_sticker, 'rb')}
1845 | addr = command + "?user_id=" + user_id_str
1846 |
1847 | if file_data is None:
1848 | return self.request.post(addr)
1849 | else:
1850 | return self.request.postFile(addr, file_data)
1851 |
1852 | def createNewStickerSet(self, user_id, name, title, emojis, png_sticker=None, tgs_sticker=None,
1853 | contains_masks=None, mask_position=None):
1854 | """
1855 | 使用此方法可以创建用户拥有的新贴纸集
1856 | 机器人将能够编辑由此创建的贴纸集
1857 | png_sticker或tgs_sticker字段只能且必须存在一个
1858 | """
1859 | command = inspect.stack()[0].function
1860 | addr = command + "?user_id=" + str(user_id)
1861 | addr += "&name=" + str(name)
1862 | addr += "&title=" + str(title)
1863 | addr += "&emojis=" + str(emojis)
1864 |
1865 | if png_sticker is None and tgs_sticker is None:
1866 | return False
1867 | elif png_sticker is not None and tgs_sticker is not None:
1868 | return False
1869 | else:
1870 | if png_sticker is not None:
1871 | if png_sticker[:7] == "http://" or png_sticker[:7] == "https:/":
1872 | file_data = None
1873 | addr += "&png_sticker=" + png_sticker
1874 | elif type(png_sticker) == bytes:
1875 | file_data = {"png_sticker": png_sticker}
1876 | elif type(png_sticker) == str and '.' not in png_sticker:
1877 | file_data = None
1878 | addr += "&png_sticker=" + png_sticker
1879 | else:
1880 | file_data = {"png_sticker": open(png_sticker, 'rb')}
1881 | elif tgs_sticker is not None:
1882 | if tgs_sticker[:7] == "http://" or tgs_sticker[:7] == "https:/":
1883 | file_data = None
1884 | addr += "&tgs_sticker=" + tgs_sticker
1885 | elif type(tgs_sticker) == bytes:
1886 | file_data = {"tgs_sticker": tgs_sticker}
1887 | elif type(tgs_sticker) == str and '.' not in tgs_sticker:
1888 | file_data = None
1889 | addr += "&tgs_sticker=" + tgs_sticker
1890 | else:
1891 | file_data = {"tgs_sticker": open(tgs_sticker, 'rb')}
1892 |
1893 | if contains_masks is not None:
1894 | addr += "&contains_masks=" + str(contains_masks)
1895 | if mask_position is not None:
1896 | addr += "&mask_position=" + json.dumps(mask_position)
1897 | else:
1898 | return False
1899 |
1900 | if file_data is None:
1901 | return self.request.post(addr)
1902 | else:
1903 | return self.request.postFile(addr, file_data)
1904 |
1905 | def addStickerToSet(self, user_id, name, emojis, png_sticker=None, tgs_sticker=None,
1906 | mask_position=None):
1907 | """
1908 | 使用此方法可以将新标签添加到由机器人创建的集合中
1909 | png_sticker或tgs_sticker字段只能且必须存在一个。
1910 | 可以将动画贴纸添加到动画贴纸集中,并且只能添加到它们
1911 | 动画贴纸集最多可以包含50个贴纸。 静态贴纸集最多可包含120个贴纸
1912 | """
1913 | command = inspect.stack()[0].function
1914 | addr = command + "?user_id=" + str(user_id)
1915 | addr += "&name=" + str(name)
1916 | addr += "&emojis=" + str(emojis)
1917 |
1918 | if png_sticker is None and tgs_sticker is None:
1919 | return False
1920 | elif png_sticker is not None and tgs_sticker is not None:
1921 | return False
1922 | else:
1923 | if png_sticker is not None:
1924 | if png_sticker[:7] == "http://" or png_sticker[:7] == "https:/":
1925 | file_data = None
1926 | addr += "&png_sticker=" + png_sticker
1927 | elif type(png_sticker) == bytes:
1928 | file_data = {"png_sticker": png_sticker}
1929 | elif type(png_sticker) == str and '.' not in png_sticker:
1930 | file_data = None
1931 | addr += "&png_sticker=" + png_sticker
1932 | else:
1933 | file_data = {"png_sticker": open(png_sticker, 'rb')}
1934 | elif tgs_sticker is not None:
1935 | if tgs_sticker[:7] == "http://" or tgs_sticker[:7] == "https:/":
1936 | file_data = None
1937 | addr += "&tgs_sticker=" + tgs_sticker
1938 | elif type(tgs_sticker) == bytes:
1939 | file_data = {"tgs_sticker": tgs_sticker}
1940 | elif type(tgs_sticker) == str and '.' not in tgs_sticker:
1941 | file_data = None
1942 | addr += "&tgs_sticker=" + tgs_sticker
1943 | else:
1944 | file_data = {"tgs_sticker": open(tgs_sticker, 'rb')}
1945 |
1946 | if mask_position is not None:
1947 | addr += "&mask_position=" + json.dumps(mask_position)
1948 |
1949 | if file_data is None:
1950 | return self.request.post(addr)
1951 | else:
1952 | return self.request.postFile(addr, file_data)
1953 |
1954 | def setStickerPositionInSet(self, sticker, position):
1955 | """
1956 | 使用此方法将机器人创建的一组贴纸移动到特定位置
1957 | """
1958 | command = inspect.stack()[0].function
1959 | addr = command + "?sticker=" + str(sticker)
1960 | addr += "&position=" + str(position)
1961 |
1962 | return self.request.post(addr)
1963 |
1964 | def deleteStickerFromSet(self, sticker):
1965 | """
1966 | 使用此方法从机器人创建的集合中删除贴纸
1967 | """
1968 | command = inspect.stack()[0].function
1969 | addr = command + "?sticker=" + str(sticker)
1970 |
1971 | return self.request.post(addr)
1972 |
1973 | def setStickerSetThumb(self, name, user_id, thumb=None):
1974 | """
1975 | 使用此方法设置贴纸集的缩略图
1976 | 只能为动画贴纸集设置动画缩略图
1977 | """
1978 | command = inspect.stack()[0].function
1979 | addr = command + "?name=" + str(name)
1980 | addr += "&user_id=" + str(user_id)
1981 |
1982 | if thumb is not None:
1983 | if thumb[:7] == "http://" or thumb[:7] == "https:/":
1984 | file_data = None
1985 | addr += "&thumb=" + thumb
1986 | elif type(thumb) == bytes:
1987 | file_data = {"thumb": thumb}
1988 | elif type(thumb) == str and '.' not in thumb:
1989 | file_data = None
1990 | addr += "&thumb=" + thumb
1991 | else:
1992 | file_data = {"thumb": open(thumb, 'rb')}
1993 |
1994 | if file_data is None:
1995 | return self.request.post(addr)
1996 | else:
1997 | return self.request.postFile(addr, file_data)
1998 |
1999 | # Payments
2000 | def sendInvoice(self, chat_id, title, description, payload, provider_token, start_parameter,
2001 | currency, prices, provider_data=None, photo_url=None,
2002 | photo_size=None, photo_width=None, photo_height=None,
2003 | need_name=None, need_phone_number=None, need_email=None,
2004 | need_shipping_address=None, send_phone_number_to_provider=None,
2005 | send_email_to_provider=None, is_flexible=None, disable_notification=None,
2006 | reply_to_message_id=None, reply_markup=None,
2007 | allow_sending_without_reply=None):
2008 | """
2009 | 使用此方法发送发票
2010 | """
2011 | command = inspect.stack()[0].function
2012 | addr = command + "?chat_id=" + str(chat_id)
2013 | addr += "&title=" + str(title)
2014 | addr += "&description=" + str(description)
2015 | addr += "&payload" + str(payload)
2016 | addr += "&provider_token=" + str(provider_token)
2017 | addr += "&start_parameter=" + str(start_parameter)
2018 | addr += "¤cy=" + str(currency)
2019 | addr += "&prices=" + json.dumps(prices)
2020 |
2021 | if provider_data is not None:
2022 | addr += "&provider_data=" + str(provider_data)
2023 | if photo_url is not None:
2024 | addr += "&photo_url=" + str(photo_url)
2025 | if photo_size is not None:
2026 | addr += "&photo_size=" + str(photo_size)
2027 | if photo_width is not None:
2028 | addr += "&photo_width=" + str(photo_width)
2029 | if photo_height is not None:
2030 | addr += "&photo_height=" + str(photo_height)
2031 | if need_name is not None:
2032 | addr += "&need_name=" + str(need_name)
2033 | if need_phone_number is not None:
2034 | addr += "&need_phone_number=" + str(need_phone_number)
2035 | if need_email is not None:
2036 | addr += "&need_email=" + str(need_email)
2037 | if need_shipping_address is not None:
2038 | addr += "&need_shipping_address=" + str(need_shipping_address)
2039 | if send_phone_number_to_provider is not None:
2040 | addr += "&send_phone_number_to_provider=" + \
2041 | str(send_phone_number_to_provider)
2042 | if send_email_to_provider is not None:
2043 | addr += "&send_email_to_provider=" + str(send_email_to_provider)
2044 | if is_flexible is not None:
2045 | addr += "&is_flexible=" + str(is_flexible)
2046 | if disable_notification is not None:
2047 | addr += "&disable_notification=" + str(disable_notification)
2048 | if reply_to_message_id is not None:
2049 | addr += "&reply_to_message_id=" + str(reply_to_message_id)
2050 | if reply_markup is not None:
2051 | addr += "&reply_markup=" + json.dumps(reply_markup)
2052 | if allow_sending_without_reply is not None:
2053 | addr += "&allow_sending_without_reply=" + str(allow_sending_without_reply)
2054 |
2055 | return self.request.post(addr)
2056 |
2057 | def answerShippingQuery(self, shipping_query_id, ok, shipping_options=None, error_message=None):
2058 | """
2059 | 使用此方法可以答复运输查询
2060 | """
2061 | command = inspect.stack()[0].function
2062 | addr = command + "?shipping_query_id=" + str(shipping_query_id)
2063 | addr += "&ok=" + str(ok)
2064 |
2065 | if shipping_options is not None:
2066 | addr += "&shipping_options=" + json.dumps(shipping_options)
2067 | if error_message is not None:
2068 | addr += "&error_message=" + str(error_message)
2069 |
2070 | return self.request.post(addr)
2071 |
2072 | def answerPreCheckoutQuery(self, pre_checkout_query_id, ok, error_message=None):
2073 | """
2074 | 使用此方法来响应此类预结帐查询
2075 | """
2076 | command = inspect.stack()[0].function
2077 | addr = command + "?pre_checkout_query_id=" + str(pre_checkout_query_id)
2078 | addr += "&ok=" + str(ok)
2079 |
2080 | if error_message is not None:
2081 | addr += "&error_message=" + str(error_message)
2082 |
2083 | return self.request.post(addr)
2084 |
2085 | # Telegram Passport
2086 |
2087 | def setPassportDataErrors(self, user_id, errors):
2088 | """
2089 | 通知用户他们提供的某些Telegram Passport元素包含错误
2090 | 在错误纠正之前,用户将无法重新提交其护照
2091 | (错误返回字段的内容必须更改)
2092 | """
2093 | command = inspect.stack()[0].function
2094 | addr = command + "?user_id=" + str(user_id)
2095 | addr += "&errors=" + json.dumps(errors)
2096 |
2097 | return self.request.post(addr)
2098 |
2099 | # Games
2100 |
2101 | def sendGame(self, chat_id, game_short_name, disable_notification=None,
2102 | reply_to_message_id=None, reply_markup=None,
2103 | allow_sending_without_reply=None):
2104 | """
2105 | 使用此方法发送游戏
2106 | """
2107 | command = inspect.stack()[0].function
2108 | addr = command + "?chat_id=" + str(chat_id)
2109 | addr += "&game_short_name=" + str(game_short_name)
2110 |
2111 | if disable_notification is not None:
2112 | addr += "&disable_notification=" + str(disable_notification)
2113 | if reply_to_message_id is not None:
2114 | addr += "&reply_to_message_id=" + str(reply_to_message_id)
2115 | if reply_markup is not None:
2116 | addr += "&reply_markup=" + json.dumps(reply_markup)
2117 | if allow_sending_without_reply is not None:
2118 | addr += "&allow_sending_without_reply=" + str(allow_sending_without_reply)
2119 |
2120 | return self.request.post(addr)
2121 |
2122 | def setGameScore(self, user_id, score, force=None, disable_edit_message=None,
2123 | chat_id=None, message_id=None, inline_message_id=None):
2124 | """
2125 | 使用此方法设置游戏中指定用户的分数
2126 | 在未指定inline_message_id的时候chat_id和message_id为必须存在的参数
2127 | """
2128 | command = inspect.stack()[0].function
2129 |
2130 | if inline_message_id is None:
2131 | if message_id is None or chat_id is None:
2132 | return False
2133 |
2134 | if inline_message_id is not None:
2135 | addr = command + "?inline_message_id=" + str(inline_message_id)
2136 | else:
2137 | addr = command + "?chat_id=" + str(chat_id)
2138 | addr += "&message_id=" + str(message_id)
2139 |
2140 | addr += "&user_id=" + str(user_id)
2141 | addr += "&score=" + str(score)
2142 |
2143 | if force is not None:
2144 | addr += "&force=" + str(force)
2145 | if disable_edit_message is not None:
2146 | addr += "&disable_edit_message=" + str(disable_edit_message)
2147 |
2148 | return self.request.post(addr)
2149 |
2150 | def getGameHighScores(self, user_id, chat_id=None, message_id=None, inline_message_id=None):
2151 | """
2152 | 使用此方法获取高分表的数据
2153 | 将返回指定用户及其在游戏中几个邻居的分数
2154 | 在未指定inline_message_id的时候chat_id和message_id为必须存在的参数
2155 | """
2156 | command = inspect.stack()[0].function
2157 |
2158 | if inline_message_id is None:
2159 | if message_id is None or chat_id is None:
2160 | return False
2161 |
2162 | if inline_message_id is not None:
2163 | addr = command + "?inline_message_id=" + str(inline_message_id)
2164 | else:
2165 | addr = command + "?chat_id=" + str(chat_id)
2166 | addr += "&message_id=" + str(message_id)
2167 |
2168 | addr += "&user_id=" + str(user_id)
2169 |
2170 | return self.request.post(addr)
2171 |
--------------------------------------------------------------------------------
/teelebot/version.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | """
3 | @description:基于Telegram Bot Api 的机器人框架
4 | @creation date: 2019-11-15
5 | @last modify: 2020-11-27
6 | @author: Pluto (github:plutobell)
7 | @version: 1.14.1
8 | """
9 |
10 | __version__ = "1.14.1"
11 | __author__ = "Pluto"
12 | __email__ = "hi@ojoll.com"
13 | __blog__ = "https://ojoll.com"
14 | __github__ = "https://github.com/plutobell/teelebot"
15 | __description__ = "teelebot is a robot framework based on Telegram Bot API, with plug-in system, easy to extend."
--------------------------------------------------------------------------------
/teelebot/webhook.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf-8 -*-
2 | '''
3 | @creation date: 2020-6-12
4 | @last modify: 2020-11-23
5 | '''
6 | from http.server import HTTPServer, BaseHTTPRequestHandler
7 | #from socketserver import ThreadingMixIn
8 | import ssl
9 | import sys
10 | import json
11 |
12 |
13 | def __MakeRequestHandler(bot):
14 | class RequestHandler(BaseHTTPRequestHandler):
15 | def __init__(self, *args, **kwargs):
16 | super(RequestHandler, self).__init__(*args, **kwargs)
17 |
18 | def do_POST(self):
19 | if self.command == "POST" and self.path == "/bot" + str(bot._key):
20 | req_data = self.rfile.read(int(self.headers['content-length']))
21 | res = req_data.decode('utf-8')
22 |
23 | message = json.loads(res)
24 | results = [message]
25 | messages = bot._washUpdates(results)
26 | if messages is not None and messages:
27 | for message in messages:
28 | bot._pluginRun(bot, message)
29 |
30 | data = {'status': 'ok'}
31 | data = json.dumps(data)
32 | self.send_response(200)
33 | self.send_header('Content-type', 'application/json')
34 | self.end_headers()
35 | self.wfile.write(data.encode('utf-8'))
36 | else:
37 | data = {'status': 'false'}
38 | data = json.dumps(data)
39 | self.send_response(400)
40 | self.send_header('Content-type', 'application/json')
41 | self.end_headers()
42 | self.wfile.write(data.encode('utf-8'))
43 |
44 | def log_message(self, format, *args):
45 | pass
46 |
47 | return RequestHandler
48 |
49 |
50 | # class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
51 | # pass
52 |
53 |
54 | def _runWebhook(bot, host, port):
55 | RequestHandler = __MakeRequestHandler(bot)
56 | if bot._local_address == "0.0.0.0":
57 | try:
58 | context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
59 | context.load_cert_chain(bot._cert_pub, bot._cert_key)
60 |
61 | server = HTTPServer((host, port), RequestHandler)
62 | server.socket = context.wrap_socket(server.socket, server_side=True)
63 | server.serve_forever()
64 | except KeyboardInterrupt:
65 | server.server_close()
66 | sys.exit("Bot Exit.")
67 | else:
68 | try:
69 | server = HTTPServer((host, port), RequestHandler)
70 | server.serve_forever()
71 | except KeyboardInterrupt:
72 | server.server_close()
73 | sys.exit("Bot Exit.")
74 |
--------------------------------------------------------------------------------