Добавлена таблица chats и флаг active.
Оптимизация запросов к VK API. Отдельные сообщения вместо вывода пустых списков. Склонение имен пользователей. Согласование слова "предупреждение" с числом перед ним.
This commit is contained in:
parent
eaf316ac84
commit
42b85d4417
3 changed files with 158 additions and 48 deletions
173
bot.py
173
bot.py
|
|
@ -2,13 +2,16 @@ import asyncio
|
||||||
import calendar
|
import calendar
|
||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from vkbottle.bot import Bot, Message
|
from vkbottle.bot import Bot, Message
|
||||||
from vkbottle_types.objects import MessagesGetConversationMembers
|
from vkbottle_types.objects import MessagesGetConversationMembers
|
||||||
|
from vkbottle.tools.formatting import bold
|
||||||
|
|
||||||
import config
|
import config
|
||||||
from config import config_load
|
from config import config_load
|
||||||
import database
|
import database
|
||||||
|
import utils
|
||||||
|
|
||||||
|
|
||||||
config_load()
|
config_load()
|
||||||
|
|
@ -19,13 +22,6 @@ def posix_time():
|
||||||
return calendar.timegm(time.gmtime())
|
return calendar.timegm(time.gmtime())
|
||||||
|
|
||||||
|
|
||||||
async def vk_get_user_info(user_id):
|
|
||||||
info = await bot.api.users.get(user_ids=[user_id])
|
|
||||||
if len(info) > 0:
|
|
||||||
return info[0]
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def vk_user_is_admin(user_id: int, chat_members: MessagesGetConversationMembers):
|
def vk_user_is_admin(user_id: int, chat_members: MessagesGetConversationMembers):
|
||||||
for member in chat_members.items:
|
for member in chat_members.items:
|
||||||
if member.member_id != user_id:
|
if member.member_id != user_id:
|
||||||
|
|
@ -34,125 +30,212 @@ def vk_user_is_admin(user_id: int, chat_members: MessagesGetConversationMembers)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def create_chat_if_not_exists(chat_id: str):
|
||||||
|
chat = database.DB.get_chat(chat_id)
|
||||||
|
if chat is None:
|
||||||
|
database.DB.add_chat(chat_id)
|
||||||
|
chat = database.DB.get_chat(chat_id)
|
||||||
|
return chat
|
||||||
|
|
||||||
|
|
||||||
def create_user_if_not_exists(chat_id: str, user_id: str):
|
def create_user_if_not_exists(chat_id: str, user_id: str):
|
||||||
res = database.DB.get_user(chat_id, user_id)
|
user = database.DB.get_user(chat_id, user_id)
|
||||||
if res is None:
|
if user is None:
|
||||||
database.DB.add_user(chat_id, user_id)
|
database.DB.add_user(chat_id, user_id)
|
||||||
|
user = database.DB.get_user(chat_id, user_id)
|
||||||
|
return user
|
||||||
|
|
||||||
|
|
||||||
|
MESSAGE_CHAT_NOT_ACTIVE = 'Извините, но я пока не работаю в этом чате.'
|
||||||
|
MESSAGE_PERMISSION_DENIED = 'Извините, но о таком меня может попросить только администратор чата.'
|
||||||
|
MESSAGE_NEED_REPLY = 'Извините, но эту команду нужно вызывать в ответном сообщении.'
|
||||||
|
|
||||||
|
|
||||||
@bot.on.chat_message(text="!старт")
|
@bot.on.chat_message(text="!старт")
|
||||||
async def start_handler(message: Message):
|
async def start_handler(message: Message):
|
||||||
chat_members = await bot.api.messages.get_conversation_members(peer_id=message.peer_id, extended=False)
|
chat_id = message.peer_id
|
||||||
|
create_chat_if_not_exists(chat_id)
|
||||||
|
|
||||||
|
chat_members = await bot.api.messages.get_conversation_members(peer_id=chat_id, extended=False)
|
||||||
|
|
||||||
if not vk_user_is_admin(message.from_id, chat_members):
|
if not vk_user_is_admin(message.from_id, chat_members):
|
||||||
await message.answer('О таком меня может попросить только администратор беседы.')
|
await message.answer(MESSAGE_PERMISSION_DENIED)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
database.DB.chat_update(chat_id, active=1)
|
||||||
|
|
||||||
for member in chat_members.items:
|
for member in chat_members.items:
|
||||||
# Пропустить ботов
|
# Пропустить ботов
|
||||||
if member.member_id < 0:
|
if member.member_id < 0:
|
||||||
continue
|
continue
|
||||||
create_user_if_not_exists(message.peer_id, member.member_id)
|
create_user_if_not_exists(chat_id, member.member_id)
|
||||||
|
|
||||||
await message.answer('Готова к работе!')
|
await message.answer('Готова к работе!')
|
||||||
|
|
||||||
|
|
||||||
@bot.on.chat_message(text="!предупреждение")
|
@bot.on.chat_message(text="!предупреждение")
|
||||||
async def warning_handler(message: Message):
|
async def warning_handler(message: Message):
|
||||||
chat_members = await bot.api.messages.get_conversation_members(peer_id=message.peer_id, extended=False)
|
chat_id = message.peer_id
|
||||||
|
chat = create_chat_if_not_exists(chat_id)
|
||||||
|
if chat['active'] == 0:
|
||||||
|
await message.answer(MESSAGE_CHAT_NOT_ACTIVE)
|
||||||
|
return
|
||||||
|
|
||||||
|
chat_members = await bot.api.messages.get_conversation_members(peer_id=chat_id, extended=False)
|
||||||
|
|
||||||
if not vk_user_is_admin(message.from_id, chat_members):
|
if not vk_user_is_admin(message.from_id, chat_members):
|
||||||
await message.answer('О таком меня может попросить только администратор беседы.')
|
await message.answer(MESSAGE_PERMISSION_DENIED)
|
||||||
return
|
return
|
||||||
|
|
||||||
if message.reply_message is None:
|
if message.reply_message is None:
|
||||||
await message.answer('Эту команду нужно вызывать в ответном сообщении.')
|
await message.answer(MESSAGE_NEED_REPLY)
|
||||||
return
|
return
|
||||||
|
|
||||||
chat_id = message.peer_id
|
|
||||||
user_id = message.reply_message.from_id
|
user_id = message.reply_message.from_id
|
||||||
create_user_if_not_exists(chat_id, user_id)
|
create_user_if_not_exists(chat_id, user_id)
|
||||||
|
|
||||||
database.DB.user_increment_warnings(chat_id, user_id)
|
database.DB.user_increment_warnings(chat_id, user_id)
|
||||||
user = database.DB.get_user(chat_id, user_id)
|
user = database.DB.get_user(chat_id, user_id)
|
||||||
|
|
||||||
user_info = await vk_get_user_info(user_id)
|
user_info = await bot.api.users.get(user_ids=[user_id], name_case='gen')
|
||||||
if user_info is not None:
|
if len(user_info) == 1:
|
||||||
await message.answer('У участника {} {} {} предупреждений.'.format(
|
await message.answer('У {} {} {} {}.'.format(
|
||||||
user_info.first_name,
|
user_info[0].first_name,
|
||||||
user_info.last_name,
|
user_info[0].last_name,
|
||||||
user['warnings'])
|
user['warnings'],
|
||||||
|
utils.make_word_agree_with_number(user['warnings'], 'предупреждение'))
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@bot.on.chat_message(text="!предупреждения")
|
@bot.on.chat_message(text="!предупреждения")
|
||||||
async def warnings_handler(message: Message):
|
async def warnings_handler(message: Message):
|
||||||
top_users = database.DB.get_top_warnings(message.peer_id)
|
chat_id = message.peer_id
|
||||||
|
chat = create_chat_if_not_exists(chat_id)
|
||||||
|
if chat['active'] == 0:
|
||||||
|
await message.answer(MESSAGE_CHAT_NOT_ACTIVE)
|
||||||
|
return
|
||||||
|
|
||||||
response = '* Участники с предупреждениями:\n'
|
top_users = database.DB.get_top_warnings(chat_id)
|
||||||
|
if len(top_users) == 0:
|
||||||
|
await message.answer('Пока все спокойно. Продолжайте в том же духе.')
|
||||||
|
return
|
||||||
|
|
||||||
|
top_user_ids = [user['user_id'] for user in top_users]
|
||||||
|
users_info = await bot.api.users.get(user_ids=top_user_ids)
|
||||||
|
|
||||||
|
response = bold('Участники с предупреждениями') + '\n'
|
||||||
i = 1
|
i = 1
|
||||||
for user in top_users:
|
for user in top_users:
|
||||||
info = await vk_get_user_info(user['user_id'])
|
for info in users_info:
|
||||||
response += '{}. {} {} - {}\n'.format(i, info.first_name, info.last_name, user['warnings'])
|
if info.id == user['user_id']:
|
||||||
i = i + 1
|
response += '{}. {} {} - {}\n'.format(i, info.first_name, info.last_name, user['warnings'])
|
||||||
|
i = i + 1
|
||||||
|
break
|
||||||
|
|
||||||
await message.answer(response)
|
await message.answer(response)
|
||||||
|
|
||||||
|
|
||||||
@bot.on.chat_message(text="!сегодня")
|
@bot.on.chat_message(text="!сегодня")
|
||||||
async def stats_today_handler(message: Message):
|
async def stats_today_handler(message: Message):
|
||||||
top_users = database.DB.get_top_messages_today(message.peer_id)
|
chat_id = message.peer_id
|
||||||
|
chat = create_chat_if_not_exists(chat_id)
|
||||||
|
if chat['active'] == 0:
|
||||||
|
await message.answer(MESSAGE_CHAT_NOT_ACTIVE)
|
||||||
|
return
|
||||||
|
|
||||||
response = '* Статистика за сегодня:\n'
|
top_users = database.DB.get_top_messages_today(chat_id)
|
||||||
|
if len(top_users) == 0:
|
||||||
|
await message.answer('Сегодня еще никто не писал. Вы можете стать первым!')
|
||||||
|
return
|
||||||
|
|
||||||
|
top_user_ids = [user['user_id'] for user in top_users]
|
||||||
|
users_info = await bot.api.users.get(user_ids=top_user_ids)
|
||||||
|
|
||||||
|
response = bold('Статистика за сегодня') + '\n'
|
||||||
i = 1
|
i = 1
|
||||||
for user in top_users:
|
for user in top_users:
|
||||||
info = await vk_get_user_info(user['user_id'])
|
for info in users_info:
|
||||||
response += '{}. {} {} - {}\n'.format(i, info.first_name, info.last_name, user['messages_today'])
|
if info.id == user['user_id']:
|
||||||
i = i + 1
|
response += '{}. {} {} - {}\n'.format(i, info.first_name, info.last_name, user['messages_today'])
|
||||||
|
i = i + 1
|
||||||
|
break
|
||||||
|
|
||||||
await message.answer(response)
|
await message.answer(response)
|
||||||
|
|
||||||
|
|
||||||
@bot.on.chat_message(text="!месяц")
|
@bot.on.chat_message(text="!месяц")
|
||||||
async def stats_month_handler(message: Message):
|
async def stats_month_handler(message: Message):
|
||||||
top_users = database.DB.get_top_messages_month(message.peer_id)
|
chat_id = message.peer_id
|
||||||
|
chat = create_chat_if_not_exists(chat_id)
|
||||||
|
if chat['active'] == 0:
|
||||||
|
await message.answer(MESSAGE_CHAT_NOT_ACTIVE)
|
||||||
|
return
|
||||||
|
|
||||||
response = '* Статистика за месяц:\n'
|
top_users = database.DB.get_top_messages_month(chat_id)
|
||||||
|
if len(top_users) == 0:
|
||||||
|
await message.answer('В этом месяце еще никто не писал. Вы можете стать первым!')
|
||||||
|
return
|
||||||
|
|
||||||
|
top_user_ids = [user['user_id'] for user in top_users]
|
||||||
|
users_info = await bot.api.users.get(user_ids=top_user_ids)
|
||||||
|
|
||||||
|
response = bold('Статистика за месяц') + '\n'
|
||||||
i = 1
|
i = 1
|
||||||
for user in top_users:
|
for user in top_users:
|
||||||
info = await vk_get_user_info(user['user_id'])
|
for info in users_info:
|
||||||
response += '{}. {} {} - {}\n'.format(i, info.first_name, info.last_name, user['messages_month'])
|
if info.id == user['user_id']:
|
||||||
i = i + 1
|
response += '{}. {} {} - {}\n'.format(i, info.first_name, info.last_name, user['messages_month'])
|
||||||
|
i = i + 1
|
||||||
|
break
|
||||||
|
|
||||||
await message.answer(response)
|
await message.answer(response)
|
||||||
|
|
||||||
|
|
||||||
@bot.on.chat_message(text="!молчуны")
|
@bot.on.chat_message(text="!молчуны")
|
||||||
async def silent_handler(message: Message):
|
async def silent_handler(message: Message):
|
||||||
|
chat_id = message.peer_id
|
||||||
|
chat = create_chat_if_not_exists(chat_id)
|
||||||
|
if chat['active'] == 0:
|
||||||
|
await message.answer(MESSAGE_CHAT_NOT_ACTIVE)
|
||||||
|
return
|
||||||
|
|
||||||
now = posix_time()
|
now = posix_time()
|
||||||
threshold = now - 14 * 24 * 3600
|
threshold = now - 14 * 24 * 3600
|
||||||
top_users = database.DB.get_top_silent(message.peer_id, threshold)
|
top_users = database.DB.get_top_silent(message.peer_id, threshold)
|
||||||
|
if len(top_users) == 0:
|
||||||
|
await message.answer('Молчунов нет. Все молодцы!')
|
||||||
|
return
|
||||||
|
|
||||||
response = '* Молчуны чата (не писали больше 2 недель):\n'
|
top_user_ids = [user['user_id'] for user in top_users]
|
||||||
|
users_info = await bot.api.users.get(user_ids=top_user_ids)
|
||||||
|
|
||||||
|
response = bold('Молчуны чата') + ' (не писали больше 2 недель)\n'
|
||||||
i = 1
|
i = 1
|
||||||
for user in top_users:
|
for user in top_users:
|
||||||
info = await vk_get_user_info(user['user_id'])
|
for info in users_info:
|
||||||
if user['last_message'] == 0:
|
if info.id == user['user_id']:
|
||||||
response += '{}. {} {} - никогда\n'.format(i, info.first_name, info.last_name)
|
if user['last_message'] == 0:
|
||||||
else:
|
response += '{}. {} {} - никогда\n'.format(i, info.first_name, info.last_name)
|
||||||
days_silent = round((now - user['last_message']) / 3600 / 24)
|
else:
|
||||||
response += '{}. {} {} - {} дней\n'.format(i, info.first_name, info.last_name, days_silent)
|
days_silent = round((now - user['last_message']) / 3600 / 24)
|
||||||
|
response += '{}. {} {} - {} дней\n'.format(i, info.first_name, info.last_name, days_silent)
|
||||||
|
i = i + 1
|
||||||
|
break
|
||||||
|
|
||||||
await message.answer(response)
|
await message.answer(response)
|
||||||
|
|
||||||
|
|
||||||
@bot.on.chat_message()
|
@bot.on.chat_message()
|
||||||
async def any_message_handler(message: Message):
|
async def any_message_handler(message: Message):
|
||||||
|
chat_id = message.peer_id
|
||||||
|
chat = create_chat_if_not_exists(chat_id)
|
||||||
|
if chat['active'] == 0:
|
||||||
|
return
|
||||||
|
|
||||||
# Игнорировать ботов
|
# Игнорировать ботов
|
||||||
if message.from_id < 0:
|
if message.from_id < 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
chat_id = message.peer_id
|
|
||||||
user_id = message.from_id
|
user_id = message.from_id
|
||||||
create_user_if_not_exists(chat_id, user_id)
|
create_user_if_not_exists(chat_id, user_id)
|
||||||
database.DB.user_set_last_message(chat_id, user_id, posix_time())
|
database.DB.user_set_last_message(chat_id, user_id, posix_time())
|
||||||
|
|
|
||||||
24
database.py
24
database.py
|
|
@ -8,6 +8,13 @@ class Database:
|
||||||
self.conn.row_factory = sqlite3.Row
|
self.conn.row_factory = sqlite3.Row
|
||||||
self.cursor = self.conn.cursor()
|
self.cursor = self.conn.cursor()
|
||||||
|
|
||||||
|
self.cursor.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS chats (
|
||||||
|
"id" INTEGER,
|
||||||
|
"active" INTEGER NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY("chat_id"))
|
||||||
|
""")
|
||||||
|
|
||||||
self.cursor.execute("""
|
self.cursor.execute("""
|
||||||
CREATE TABLE IF NOT EXISTS users (
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
"chat_id" INTEGER,
|
"chat_id" INTEGER,
|
||||||
|
|
@ -18,6 +25,20 @@ class Database:
|
||||||
"warnings" INTEGER NOT NULL DEFAULT 0,
|
"warnings" INTEGER NOT NULL DEFAULT 0,
|
||||||
PRIMARY KEY("chat_id","user_id"))
|
PRIMARY KEY("chat_id","user_id"))
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
self.conn.commit()
|
||||||
|
|
||||||
|
def get_chat(self, chat_id: int):
|
||||||
|
self.cursor.execute("SELECT * FROM chats WHERE id = ?", (chat_id,))
|
||||||
|
return self.cursor.fetchone()
|
||||||
|
|
||||||
|
def add_chat(self, chat_id: int):
|
||||||
|
self.cursor.execute("INSERT INTO chats (id) VALUES (?)", (chat_id,))
|
||||||
|
self.conn.commit()
|
||||||
|
|
||||||
|
def chat_update(self, chat_id: int, **kwargs):
|
||||||
|
self.cursor.execute("UPDATE chats SET " + ", ".join(f + " = :" + f for f in kwargs) +
|
||||||
|
f" WHERE id = {chat_id}", kwargs)
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
|
|
||||||
def get_user(self, chat_id: int, user_id: int):
|
def get_user(self, chat_id: int, user_id: int):
|
||||||
|
|
@ -48,9 +69,6 @@ class Database:
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
|
|
||||||
def user_update(self, chat_id: int, user_id: int, **kwargs):
|
def user_update(self, chat_id: int, user_id: int, **kwargs):
|
||||||
# query = ("UPDATE users SET " + ", ".join(f + " = :" + f for f in kwargs) +
|
|
||||||
# " WHERE chat_id = ? AND user_id = ?")
|
|
||||||
# print(query)
|
|
||||||
self.cursor.execute("UPDATE users SET " + ", ".join(f + " = :" + f for f in kwargs) +
|
self.cursor.execute("UPDATE users SET " + ", ".join(f + " = :" + f for f in kwargs) +
|
||||||
f" WHERE chat_id = {chat_id} AND user_id = {user_id}", kwargs)
|
f" WHERE chat_id = {chat_id} AND user_id = {user_id}", kwargs)
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
|
|
|
||||||
9
utils.py
Normal file
9
utils.py
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
import pymorphy3
|
||||||
|
|
||||||
|
|
||||||
|
_morph = pymorphy3.MorphAnalyzer()
|
||||||
|
|
||||||
|
|
||||||
|
def make_word_agree_with_number(n: int, word: str) -> str:
|
||||||
|
w = _morph.parse(word)[0]
|
||||||
|
return w.make_agree_with_number(n).word
|
||||||
Loading…
Add table
Reference in a new issue