-
Notifications
You must be signed in to change notification settings - Fork 1
/
fmf_bot.py
329 lines (257 loc) · 12.1 KB
/
fmf_bot.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
#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
import re
import sqlite3
import time
import aiohttp
import asyncio
from aiogram import Bot, types
from aiogram.dispatcher import Dispatcher
from aiogram.utils import executor
from aiogram.utils.exceptions import MessageTextIsEmpty
from command_parser import CommandParser
OWN_NAME = re.compile('@?fmf_robot')
NO_NICKNAME_MSG = '''К сожалению, этот бот может работать только с людьми, у которых заполнено имя пользователя в профиле. 🙁
Сделать это можно здесь:
≡ - Settings - Username или ≡ - Settings - Edit profile - Username
≡ - Настройки - Имя пользователя или ≡ - Настройки - Изменить профиль - Имя пользователя
Заполни его и приходи!
'''
HELP_MESSAGE = '''Этот бот предназначен для поиска сексуальных партнёров среди ваших друзей.
Логика работы бота такая: вы указываете людей, которым готовы сказать "я хочу секса с тобой, давай об этом поговорим". Если вы указали кого-то, а этот человек указал вас, то бот сообщит вам обоим, что ваш интерес взаимен. Сообщит он это вам и тому второму человеку фразой "У вас совпадение с @username". А дальше - пишите человеку в личку и разговаривайте!
Доступные команды:
{0}
В качестве name в командах выступает ник пользователя. Например, ник этого бота – @fmf_robot, его можно узнать, нажав на полосу с краткой информацией в верхней части экрана.
Чтобы узнать имя пользователя заинтересовавшего вас человека, находящегося рядом с вами, может пригодиться вот эта опция:
≡ - Contacts - Find People Nearby - Make Myself visible
≡ - Контакты - Найти людей рядом - Показать меня
Удачи в поисках!
'''
WORKDIR = os.path.abspath(os.path.dirname(__file__))
class NoNickname(Exception):
pass
def read_token():
token_file_name = os.path.join(WORKDIR, 'fmf_bot_token')
if not os.path.isfile(token_file_name):
token_file_name = '/root/fmf_bot_token'
with open(token_file_name, 'r') as f:
return f.read().strip()
bot = Bot(token=read_token())
dp = Dispatcher(bot)
command_parser = CommandParser(dp)
def member_in_db(connection, member_id):
cur = connection.cursor()
cur.execute('SELECT COUNT(*) FROM members WHERE id=?', (member_id,))
return cur.fetchone()[0] > 0
def add_member_to_db(connection, member_id, member_name, chat_id):
cur = connection.cursor()
# member_id and chat_id are the same, so it's just a historical issue.
cur.execute('INSERT INTO members (id, name, chat) VALUES (?, ?, ?)',
(member_id, member_name, chat_id))
connection.commit()
def member_changed_name(connection, member_id, member_name):
cur = connection.cursor()
cur.execute('SELECT COUNT(*) FROM members WHERE id=? AND name=?',
(member_id, member_name))
return cur.fetchone()[0] == 0
def update_name(connection, member_id, member_name):
cur = connection.cursor()
cur.execute('SELECT NAME FROM members WHERE id=?', (member_id,))
prev_name = cur.fetchone()[0]
cur.execute('UPDATE members SET name=?, previous_name=? WHERE id=?',
(member_name, prev_name, member_id))
connection.commit()
def add_match(connection, member_id, match_name):
cur = connection.cursor()
cur.execute('DELETE FROM matches WHERE member_id=? AND LOWER(match_name)=?',
(member_id, match_name.lower()))
cur.execute('INSERT INTO matches (member_id, match_name) VALUES (?, ?)',
(member_id, match_name))
connection.commit()
async def check_new_matches(connection, member_id, new_matches):
matches = member_matches(connection, member_id)
new_matches = ['@' + n if not n.startswith('@') else n for n in new_matches]
for match in new_matches:
if match in matches:
await congratulations_messages(connection, member_id, match)
def member_likes(connection, member_id):
cur = connection.cursor()
cur.execute('SELECT match_name FROM matches WHERE member_id=?',
(member_id,))
return [c[0] for c in cur.fetchall()]
def likes_message(connection, member_id):
likes = member_likes(connection, member_id)
if not likes:
return 'Вы пока никого не добавили в список.'
return 'Ваш список: {}'.format(', '.join(sorted(likes, key=lambda x: x.lower())))
def invalid_nicks_message(invalid_nicks):
return 'Это - не имена пользователей! Повнимательнее :)\n{}'.format(', '.join(invalid_nicks))
def member_matches(connection, member_id):
cur = connection.cursor()
cur.execute('''
SELECT DISTINCT(m1.match_name) FROM matches as m1
JOIN members as mem1 on m1.member_id = mem1.id
JOIN matches as m2 ON LOWER(mem1.name) = LOWER(m2.match_name)
JOIN members as mem2 on m2.member_id = mem2.id
WHERE m1.member_id=? AND LOWER(m1.match_name) = LOWER(mem2.name)''',
(member_id,))
return [c[0] for c in cur.fetchall()]
def is_match(connection, member_id, name):
return name in member_matches(connection, member_id)
def matches_message(connection, member_id):
matches = member_matches(connection, member_id)
if matches:
return 'У вас взаимный интерес с этими людьми: {}'.format(
', '.join(sorted(matches, key=lambda x: x.lower())))
else:
return 'Пока у вас нет взаимного интереса ни с кем, но не сдавайтесь!'
def remove_match(connection, member_id, match_name):
cur = connection.cursor()
cur.execute('DELETE FROM matches WHERE member_id=? AND LOWER(match_name)=?',
(member_id, match_name.lower()))
connection.commit()
async def congratulations_messages(connection, member_id, match):
cur = connection.cursor()
cur.execute('SELECT name, chat FROM members WHERE id=?',
(member_id,))
name, chat_id = cur.fetchone()
await bot.send_message(chat_id, 'У вас совпадение с {}. Удачи!'.format(match))
cur.execute('SELECT chat FROM members WHERE LOWER(name)=LOWER(?)',
(match,))
chat_id = cur.fetchone()[0]
await bot.send_message(chat_id, 'У вас совпадение с {}. Удачи!'.format(name))
def get_db():
db_path = os.path.join(WORKDIR, 'fmf.db')
connection = sqlite3.connect(db_path)
return connection
async def handle_nickname(message):
if message.from_user.username is None:
await message.reply(NO_NICKNAME_MSG)
raise NoNickname
member_name = '@' + message.from_user.username
member_id = message.from_user.id
connection = get_db()
if not member_in_db(connection, member_id):
add_member_to_db(connection, member_id, member_name, member_id)
elif member_changed_name(connection, member_id, member_name):
update_name(connection, member_id, member_name)
return member_name, member_id
async def add_command(message: types.Message):
try:
member_name, member_id = await handle_nickname(message)
except NoNickname:
return
params = message.get_args().split()
if any((OWN_NAME.match(p) for p in params)):
await bot.send_message(message.from_user.id, 'Это так неожиданно! 😘')
valid_nick_pattern = re.compile(r'^\@?[A-Za-z]\w{4}\w*$')
invalid_nicks = []
connection = get_db()
for match_name in params:
if not valid_nick_pattern.match(match_name):
invalid_nicks.append(match_name)
continue
if not match_name.startswith('@'):
match_name = '@' + match_name
add_match(connection, member_id, match_name)
await check_new_matches(connection, member_id, params)
msg = likes_message(connection, member_id)
if invalid_nicks:
msg = '\n'.join([msg, invalid_nicks_message(invalid_nicks)])
await message.reply(msg)
async def remove_command(message: types.Message):
try:
member_name, member_id = await handle_nickname(message)
except NoNickname:
return
params = message.get_args().split()
connection = get_db()
for name in params:
if not name.startswith('@'):
name = '@' + name
remove_match(connection, member_id, name)
await message.reply(likes_message(connection, member_id))
async def list_command(message: types.Message):
try:
member_name, member_id = await handle_nickname(message)
except NoNickname:
return
params = message.get_args().split()
connection = get_db()
await message.reply(likes_message(connection, member_id))
async def match_command(message: types.Message):
try:
member_name, member_id = await handle_nickname(message)
except NoNickname:
return
params = message.get_args().split()
connection = get_db()
await message.reply(matches_message(connection, member_id))
async def rename_command(message: types.Message):
try:
member_name, member_id = await handle_nickname(message)
except NoNickname:
return
connection = get_db()
cur = connection.cursor()
cur.execute('SELECT name, previous_name FROM members WHERE id=?',
(member_id,))
name, previous_name = cur.fetchone()
if previous_name is not None:
cur.execute('UPDATE matches SET match_name=? WHERE match_name=?',
(name, previous_name))
connection.commit()
await message.reply('OK')
async def help_message(message: types.Message):
await message.reply(HELP_MESSAGE.format(command_parser.getHelp()))
async def unknown_command(message: types.Message):
await message.reply('Команда ещё не реализована, потерпите немного.')
await message.reply(HELP_MESSAGE.format(command_parser.getHelp()))
class FmfBotCommand():
ADD = 1
REMOVE = 2
LIST = 3
MATCHES = 4
HELP = 5
RENAME = 6
def init_command_parser():
global command_parser
command_parser.registerCommand(
FmfBotCommand.ADD,
add_command,
['a', 'add', 'like'],
'Добавить в список симпатичных вам людей одного или нескольких человек',
nargs='*',
arg_name='name')
command_parser.registerCommand(
FmfBotCommand.REMOVE,
remove_command,
['rm', 'remove'],
'Удалить из списка симпатичных вам людей одного или нескольких человек',
nargs='*',
arg_name='name')
command_parser.registerCommand(
FmfBotCommand.LIST,
list_command,
['l', 'list'],
'Показать список симпатичных вам людей')
command_parser.registerCommand(
FmfBotCommand.MATCHES,
match_command,
['m', 'matches'],
'Показать список людей, с которыми у вас появилась взаимность')
command_parser.registerCommand(
FmfBotCommand.HELP,
help_message,
['h', 'help', 'start'],
'Выводит это сообщение')
command_parser.registerCommand(
FmfBotCommand.RENAME,
rename_command,
['rename'],
'Если у вас изменился ник – обновляет его у всех, кому вы симпатичны.')
dp.register_message_handler(unknown_command)
if __name__ == '__main__':
init_command_parser()
executor.start_polling(dp)