forked from a-fruity-melon/PagerMaid_Plugins
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathsendat.py
357 lines (311 loc) · 13.9 KB
/
sendat.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
""" Send Msg At A Specified Time. """
# By tg @fruitymelon
# extra requirements: dateparser
import sys, time, traceback
import asyncio
from pagermaid import log, version
from pagermaid.listener import listener
from pagermaid.utils import pip_install
pip_install("dateparser")
import dateparser
DAY_SECS = 24 * 60 * 60
def logsync(message):
sys.stdout.writelines(f"{message}\n")
logsync("sendat: loading... If failed, please install dateparser first.")
# https://stackoverflow.com/questions/1111056/get-time-zone-information-of-the-system-in-python
def local_time_offset(t=None):
"""Return offset of local zone from GMT, either at present or at time t."""
# python2.3 localtime() can't take None
if t is None:
t = time.time()
if time.localtime(t).tm_isdst and time.daylight:
return -time.altzone
else:
return -time.timezone
offset = local_time_offset() // 3600
sign = "+" if offset >= 0 else "-"
offset = abs(offset)
offset_str = str(offset)
offset_str = offset_str if len(offset_str) == 2 else f"0{offset_str}"
settings = {'TIMEZONE': f'{sign}{offset_str}00'}
logsync(f"sendat: local time zone offset is {sign}{abs(offset)}")
mem = []
helpmsg = """
定时发送消息。
-sendat 时间 | 消息内容
i.e.
-sendat 16:00:00 | 投票截止!
-sendat every 23:59:59 | 又是无所事事的一天呢。
-sendat every 1 minutes | 又过去了一分钟。
-sendat *3 1 minutes | 此消息将出现三次,间隔均为一分钟。
根据 Registered id,删除已注册的 timer:
-sendat rm 2
此命令可与 autorespond(也是我写的插件), ghost, deny 命令串联:
-sendat 10 minutes | -autorespond 我暂时有事离开一下。
"""
@listener(is_plugin=True, outgoing=True, command="sendat", diagnostics=True, ignore_edited=True,
description=helpmsg,
parameters="<atmsg>")
async def sendatwrap(context):
await sendat(context)
async def sendat(context):
if not context.parameter:
await context.edit(helpmsg)
return
mem_id = len(mem)
mem.append("[NULL]")
chat = await context.get_chat()
args = " ".join(context.parameter).split("|")
await context.edit(f"debug: tz data: {time.timezone} {time.tzname} {sign}{offset}")
if len(args) != 2:
if args[0].find("rm ") == 0:
# clear timer
target_ids = args[0][3:].strip().split(" ")
for target_id in target_ids:
if target_id.isnumeric():
if len(mem) > int(target_id):
mem[int(target_id)] = ""
await context.edit(f"id {target_id} successfully removed.")
else:
await context.edit("id out of range.")
return
else:
await context.edit("id should be a integer.")
return
else:
await context.edit("Invalid argument counts. Expecting: 2")
return
if offset is None:
await context.edit("Failed to get your server's timezone.")
return
try:
if args[0].find("every ") == 0:
# at this point, let's assume args[0] contains relative time
# i.e. -sendat every 3 minutes
time_str = args[0][6:]
if time_str.find(":") != -1:
# then it should be absolute time
target = dateparser.parse(time_str, settings=settings).timestamp() % DAY_SECS
if target >= DAY_SECS - 6:
# 太接近午夜,小概率直接 sleep 过头,特殊处理
target = 0
mem[mem_id] = "|".join(args)
await sendmsg(context, chat, f"{args[0]} -> {target} sec after 00:00:00 UTC+0")
await context.edit(f"Registered: id {mem_id}. You can use this id to cancel the timer.")
last_sent = 0
while mem[mem_id] != "":
if time.time() % DAY_SECS < target:
# 时间没到
await asyncio.sleep(2)
continue
if time.time() % DAY_SECS >= target and time.time() - last_sent < DAY_SECS - 10:
# 时间过了,第二天的没到
await asyncio.sleep(2)
continue
if mem[mem_id] != "":
await sendmsg(context, chat, args[1])
last_sent = time.time()
mem[mem_id] = ""
return
sleep_time = time.time() - dateparser.parse(time_str, settings=settings).timestamp()
if sleep_time < 5:
await context.edit(f"Sleep time too short. Should be longer than 5 seconds. Got {sleep_time}")
return
mem[mem_id] = "|".join(args)
await context.edit(f"Registered: id {mem_id}. You can use this id to cancel the timer.")
while mem[mem_id] != "":
last_time = time.time()
while time.time() < last_time + sleep_time and mem[mem_id] != "":
await asyncio.sleep(2)
if mem[mem_id] != "":
await sendmsg(context, chat, args[1])
mem[mem_id] = ""
return
elif args[0].find("*") == 0:
times = int(args[0][1:].split(" ")[0])
rest = " ".join(args[0][1:].split(" ")[1:])
if rest.find(":") != -1:
# then it should be absolute time
target = dateparser.parse(rest, settings=settings).timestamp() % DAY_SECS
if target >= DAY_SECS - 6:
# 太接近午夜,小概率直接 sleep 过头,特殊处理
target = 0
count = 0
mem[mem_id] = "|".join(args)
await context.edit(f"Registered: id {mem_id}. You can use this id to cancel the timer.")
last_sent = 0
while count <= times and mem[mem_id] != "":
if time.time() % DAY_SECS < target:
# 时间没到
await asyncio.sleep(2)
continue
if time.time() % DAY_SECS >= target and time.time() - last_sent < DAY_SECS - 10:
# 时间过了,第二天的没到
await asyncio.sleep(2)
continue
if mem[mem_id] != "":
await sendmsg(context, chat, args[1])
count += 1
last_sent = time.time()
mem[mem_id] = ""
return
sleep_time = time.time() - dateparser.parse(rest, settings=settings).timestamp()
if sleep_time < 5:
await context.edit(f"Sleep time too short. Should be longer than 5 seconds. got {sleep_time}")
return
count = 0
mem[mem_id] = "|".join(args)
await context.edit(f"Registered: id {mem_id}. You can use this id to cancel the timer.")
while count <= times and mem[mem_id] != "":
last_time = time.time()
while time.time() < last_time + sleep_time and mem[mem_id] != "":
await asyncio.sleep(2)
if mem[mem_id] != "":
await sendmsg(context, chat, args[1])
count += 1
mem[mem_id] = ""
return
if args[0].find(":") == -1:
# relative time
dt = dateparser.parse(args[0])
delta = time.time() - dt.timestamp()
if delta < 3:
await context.edit("Target time before now.")
return
mem[mem_id] = "|".join(args)
await context.edit(f"Registered: id {mem_id}. You can use this id to cancel the timer.")
while dt.timestamp() + 2 * delta > time.time() and mem[mem_id] != "":
await asyncio.sleep(2)
if mem[mem_id] != "":
await sendmsg(context, chat, args[1])
mem[mem_id] = ""
return
# absolute time
target_time = dateparser.parse(args[0], settings=settings).timestamp()
if target_time - time.time() < 3:
await context.edit("Target time before now.")
return
mem[mem_id] = "|".join(args)
await context.edit(f"Registered: id {mem_id}. You can use this id to cancel the timer.")
while target_time - time.time() > 0 and mem[mem_id] != "":
await asyncio.sleep(2)
if mem[mem_id] != "":
await sendmsg(context, chat, args[1])
mem[mem_id] = ""
except Exception as e:
await log(str(e))
await log(str(traceback.format_stack()))
return
@listener(outgoing=True, command="sendatdump", diagnostics=True, ignore_edited=True,
description="导出并转储内存中的 sendat 配置")
async def sendatdump(context):
clean_mem = mem[:]
if clean_mem.count("[NULL]") != 0:
clean_mem.remove("[NULL]")
if clean_mem.count("") != 0:
clean_mem.remove("")
await context.edit(".\n-sendat " + "\n-sendat ".join(clean_mem))
@listener(outgoing=True, command="sendatparse", diagnostics=True, ignore_edited=True,
description="导入已导出的 sendat 配置。用法:-sendatparse 在此处粘贴 -sendatdump 命令的输出结果")
async def sendatparse(context):
chat = await context.get_chat()
text = "\n".join(context.message.text.split("\n")[1:])
if text == "":
return
if text.find(".\n") == 0:
text = "\n".join(text.split("\n")[1:])
lines = text.split("\n")
pms = []
for i in range(len(lines)):
line = lines[i]
sent = await sendmsg(context, chat, line)
sent.parameter = line.replace("-sendat ", "").split(" ")
pms.append(sendat(sent))
await asyncio.wait(pms)
""" Modified pagermaid autorespond plugin. """
from telethon.events import StopPropagation
from pagermaid import persistent_vars
async def autorespond(context):
""" Enables the auto responder. """
message = "我还在睡觉... ZzZzZzZzZZz"
if context.arguments:
message = context.arguments
await context.edit("成功启用自动响应器。")
await log(f"启用自动响应器,将自动回复 `{message}`.")
persistent_vars.update({'autorespond': {'enabled': True, 'message': message, 'amount': 0}})
from pagermaid import redis, redis_status
async def ghost(context):
""" Toggles ghosting of a user. """
if not redis_status():
await context.edit("出错了呜呜呜 ~ Redis 好像离线了,无法执行命令。")
return
if len(context.parameter) != 1:
await context.edit("出错了呜呜呜 ~ 无效的参数。")
return
myself = await context.client.get_me()
self_user_id = myself.id
if context.parameter[0] == "true":
if context.chat_id == self_user_id:
await context.edit("在?为什么要在收藏夹里面用?")
return
redis.set("ghosted.chat_id." + str(context.chat_id), "true")
await context.delete()
await log(f"已成功将 ChatID {str(context.chat_id)} 添加到自动已读对话列表中。")
elif context.parameter[0] == "false":
if context.chat_id == self_user_id:
await context.edit("在?为什么要在收藏夹里面用?")
return
redis.delete("ghosted.chat_id." + str(context.chat_id))
await context.delete()
await log(f"已成功将 ChatID {str(context.chat_id)} 从自动已读对话列表中移除。")
elif context.parameter[0] == "status":
if redis.get("ghosted.chat_id." + str(context.chat_id)):
await context.edit("emm...当前对话已被加入自动已读对话列表中。")
else:
await context.edit("emm...当前对话已从自动已读对话列表中移除。")
else:
await context.edit("出错了呜呜呜 ~ 无效的参数。")
async def deny(context):
""" Toggles denying of a user. """
if not redis_status():
await context.edit("出错了呜呜呜 ~ Redis 离线,无法运行。")
return
if len(context.parameter) != 1:
await context.edit("出错了呜呜呜 ~ 无效的参数。")
return
myself = await context.client.get_me()
self_user_id = myself.id
if context.parameter[0] == "true":
if context.chat_id == self_user_id:
await context.edit("在?为什么要在收藏夹里面用?")
return
redis.set("denied.chat_id." + str(context.chat_id), "true")
await context.delete()
await log(f"ChatID {str(context.chat_id)} 已被添加到自动拒绝对话列表中。")
elif context.parameter[0] == "false":
if context.chat_id == self_user_id:
await context.edit("在?为什么要在收藏夹里面用?")
return
redis.delete("denied.chat_id." + str(context.chat_id))
await context.delete()
await log(f"ChatID {str(context.chat_id)} 已从自动拒绝对话列表中移除。")
elif context.parameter[0] == "status":
if redis.get("denied.chat_id." + str(context.chat_id)):
await context.edit("emm...当前对话已被加入自动拒绝对话列表中。")
else:
await context.edit("emm...当前对话已从自动拒绝对话列表移除。")
else:
await context.edit("出错了呜呜呜 ~ 无效的参数。")
async def sendmsg(context, chat, origin_text):
text = origin_text.strip()
msg = await context.client.send_message(chat, text)
if text.find("-autorespond") == 0:
msg.arguments = text.replace("-autorespond", "").lstrip()
await autorespond(msg)
elif text.find("-ghost ") == 0:
msg.parameter = [text[7:]]
await ghost(msg)
elif text.find("-deny ") == 0:
msg.parameter = [text[6:]]
await deny(msg)
return msg