From e7304f36b7c6d85d2fa0e05afa6c9aae00b03e3e Mon Sep 17 00:00:00 2001 From: lifegpc Date: Thu, 28 Oct 2021 21:39:30 +0800 Subject: [PATCH] add /banlist, /ban, /unban --- blackList.py | 207 +++++++++++++++++++++++++++++++++++++++++++++++---- database.py | 46 +++++++++++- rssbot.py | 199 ++++++++++++++++++++++++++++++++++++++++++++++--- textc.py | 4 + 4 files changed, 430 insertions(+), 26 deletions(-) diff --git a/blackList.py b/blackList.py index 4565f3c..4ecae63 100644 --- a/blackList.py +++ b/blackList.py @@ -15,21 +15,96 @@ # along with this program. If not, see . from re import search from typing import List +from textc import textc, timeToStr +from enum import Enum, unique +from math import ceil, floor class BlackInfo: - uid: int = None + __uid: int = None op_uid: int = None from_config: bool = False add_time: int = None reason: str = None - def __init__(self, uid: int, op_uid: int = None, add_time: int = None, reason: str = None): - self.uid = uid + @property + def uid(self) -> int: + return self.__uid + + def __init__(self, uid: int, op_uid: int = None, add_time: int = None, reason: str = None, title: str = None): + self.__uid = uid self.op_uid = op_uid self.from_config = True if self.op_uid is None else False self.add_time = add_time self.reason = reason + self.title = title + + @property + def name(self) -> str: + return f"{self.uid}" if self.title is None else self.title + + +class BlackInfoListIter: + def __init__(self, li: List[BlackInfo]): + self.__l = li + self.__start = 0 + + def __next__(self) -> BlackInfo: + if self.__start >= len(self.__l): + raise StopIteration + v = self.__l[self.__start] + self.__start += 1 + return v + + +class BlackInfoList: + def __add__(self, b): + o = BlackInfoList(self.__l) + if isinstance(b, (BlackInfoList, list)): + for i in b: + o.append(i) + elif isinstance(b, BlackInfo): + o.append(b) + return o + + def __getitem__(self, i) -> BlackInfo: + return self.__l[i] + + def __iadd__(self, b): + if isinstance(b, (BlackInfoList, list)): + for i in b: + self.append(i) + elif isinstance(b, BlackInfo): + self.append(b) + return self + + def __init__(self, li: List[BlackInfo] = None): + self.__t = [] + self.__l = [] + if li is not None: + for i in li: + self.append(i) + + def __iter__(self): + it = BlackInfoListIter(self.__l) + return it + + def __len__(self): + return len(self.__l) + + def append(self, info: BlackInfo): + if info.uid not in self.__t: + self.__l.append(info) + self.__t.append(info.uid) + + def find(self, uid: int): + if uid in self.__t: + return self.__t.index(uid) + else: + return -1 + + def isInBlackList(self, chatId: int) -> bool: + return chatId in self.__t class ConfigBlackList: @@ -43,8 +118,8 @@ class ConfigBlackList: if search(r'^[\+-]?[0-9]+$', i) is not None: self.__list.append(int(i)) - def getBlackList(self) -> List[BlackInfo]: - r = [] + def getBlackList(self) -> BlackInfoList: + r = BlackInfoList() for i in self.__list: r.append(BlackInfo(i)) return r @@ -58,23 +133,129 @@ class BlackList: from rssbot import main self._main: main = m self._configBlackList = ConfigBlackList(m, self._main._setting._blackList) + self._blackList = self._main._db.getBlackList() - def getBlackList(self) -> List[BlackInfo]: - m = self._configBlackList.getBlackList() - t = [] - r = [] - for i in m: - if i.uid not in t: - t.append(i.uid) - r.append(i) + def ban(self, i: BlackInfo): + re = self._main._db.addBlackInfo(i) + if re: + self._blackList = self._main._db.getBlackList() + self.checkRSSList() + return re + + def getBlackList(self) -> BlackInfoList: + r = self._configBlackList.getBlackList() + r += self._blackList return r def isInBlackList(self, chatId: int) -> bool: if self._configBlackList.isInBlackList(chatId): return True + if self._blackList.isInBlackList(chatId): + return True return False def checkRSSList(self): li = self.getBlackList() for i in li: self._main._db.removeChatInChatList(i.uid) + + def unban(self, userId: int): + re = self._main._db.removeFromBlackList(userId) + if re: + self._blackList = self._main._db.getBlackList() + return re + + +@unique +class InlineKeyBoardForBlackList(Enum): + FirstPage = 0 + LastPage = 1 + PrevPage = 2 + NextPage = 3 + Close = 4 + BlackInfo = 5 + BackToList = 6 + Unban = 7 + ConfirmUnban = 8 + CancleUnban = 9 + + +def getTextContentForBlackInfo(i: BlackInfo): + t = textc() + link = '' if i.uid < 0 else f'tg://user?id={i.uid}' + t += f'被封禁用户: {i.name}' + if i.from_config: + t += '来源:配置文件' + else: + t += '来源:数据库' + t += f'封禁操作人:{i.op_uid}' + t += f'封禁时间:{timeToStr(i.add_time)}' + t += f'封禁理由:{i.reason}' + return t.tostr() + + +def getTextContentForUnbanBlackInfo(i: BlackInfo): + link = '' if i.uid < 0 else f'tg://user?id={i.uid}' + return f'是否要取消封禁{i.name}?' + + +def getInlineKeyBoardForBlackList(bl: BlackInfoList, page: int = 1, lastPage: bool = False, itemIndex: int = None): + d = [] + i = -1 + lineLimit = 7 + l = len(bl) + pn = ceil(l / lineLimit) + if lastPage: + page = pn + if itemIndex is not None and itemIndex >= 0: + page = floor(itemIndex / lineLimit) + 1 + if l != 0: + page = max(min(pn, page), 1) + s = max(lineLimit * (page - 1), 0) + n = min(lineLimit * page, l) + while s < n: + d.append([]) + i += 1 + d[i].append({'text': bl[s].name, 'callback_data': f'2,{InlineKeyBoardForBlackList.BlackInfo.value},{s},{bl[s].uid}'}) + s += 1 + if pn != 1: + d.append([]) + i += 1 + if page != 1: + d[i].append({'text': '上一页', 'callback_data': f'2,{InlineKeyBoardForBlackList.PrevPage.value},{page}'}) + if page != pn: + d[i].append({'text': '下一页', 'callback_data': f'2,{InlineKeyBoardForBlackList.NextPage.value},{page}'}) + d.append([]) + i += 1 + if page != 1: + d[i].append({'text': '首页', 'callback_data': f'2,{InlineKeyBoardForBlackList.FirstPage.value}'}) + if page != pn: + d[i].append({'text': '尾页', 'callback_data': f'2,{InlineKeyBoardForBlackList.LastPage.value}'}) + d.append([]) + i += 1 + d[i].append({'text': '关闭', 'callback_data': f'2,{InlineKeyBoardForBlackList.Close.value}'}) + return {'inline_keyboard': d} + + +def getInlineKeyBoardForBlackInfo(b: BlackInfo, index: int): + d = [] + i = -1 + if not b.from_config: + d.append([]) + i += 1 + d[i].append({'text': '取消封禁', 'callback_data': f'2,{InlineKeyBoardForBlackList.Unban.value},{index},{b.uid}'}) + d.append([]) + i += 1 + d[i].append( + {'text': '返回', 'callback_data': f'2,{InlineKeyBoardForBlackList.BackToList.value},{index}'}) + return {'inline_keyboard': d} + + +def getInlineKeyBoardForUnbanBlackInfo(b: BlackInfo, index: int): + d = [] + i = -1 + d.append([]) + i += 1 + d[i].append({'text': '是', 'callback_data': f'2,{InlineKeyBoardForBlackList.ConfirmUnban.value},{index},{b.uid}'}) + d[i].append({'text': '否', 'callback_data': f'2,{InlineKeyBoardForBlackList.CancleUnban.value},{index},{b.uid}'}) + return {'inline_keyboard': d} diff --git a/database.py b/database.py index 5a042b8..dd1e71d 100644 --- a/database.py +++ b/database.py @@ -16,11 +16,11 @@ import sqlite3 from config import RSSConfig from RSSEntry import RSSEntry, ChatEntry, HashEntry, HashEntries -from typing import List +from typing import List, Tuple from enum import Enum, unique from threading import Lock -from hashl import sha256WithBase64 from time import time +from blackList import BlackInfo, BlackInfoList VERSION_TABLE = '''CREATE TABLE version ( @@ -64,6 +64,7 @@ userId INT, op_uid INT, add_time INT, reason TEXT, +name TEXT, PRIMARY KEY (userId) )''' @@ -134,6 +135,9 @@ class database: if v < [1, 0, 0, 6]: self._db.execute(USERBLACKLIST_TABLE) self._db.commit() + if v < [1, 0, 0, 7]: + self._db.execute('ALTER TABLE userBlackList ADD name TEXT;') + self._db.commit() self._db.execute('VACUUM;') self.__updateExistsTable() self.__write_version() @@ -156,7 +160,7 @@ class database: self._db.commit() def __init__(self, m, loc: str): - self._version = [1, 0, 0, 6] + self._version = [1, 0, 0, 7] self._value_lock = Lock() self._db = sqlite3.connect(loc, check_same_thread=False) self._db.execute('VACUUM;') @@ -266,6 +270,23 @@ class database: except: return False + def addBlackInfo(self, i: BlackInfo) -> bool: + with self._value_lock: + try: + have_value = False + cur = self._db.execute('SELECT * FROM userBlackList WHERE userId=?;', (i.uid,)) + for i in cur: + have_value = True + break + if have_value: + self._db.execute('UPDATE userBlackList SET op_uid=?, add_time=?, reason=?, name=? WHERE userId=?;', (i.op_uid, i.add_time, i.reason, i.title, i.uid)) + else: + self._db.execute('INSERT INTO userBlackList VALUES (?, ?, ?, ?, ?);', (i.uid, i.op_uid, i.add_time, i.reason, i.title)) + self._db.commit() + return True + except Exception: + return False + def getAllRSSList(self) -> List[RSSEntry]: with self._value_lock: cur = self._db.execute(f'SELECT * FROM RSSList;') @@ -287,6 +308,14 @@ class database: r.append(temp) return r + def getBlackList(self) -> BlackInfoList: + with self._value_lock: + cur = self._db.execute('SELECT * FROM userBlackList;') + li = BlackInfoList() + for i in cur: + li.append(BlackInfo(i[0], i[1], i[2], i[3], i[4])) + return li + def getRSSByIdAndChatId(self, id: int, chatId: int) -> RSSEntry: while self._value_lock: cur = self._db.execute('SELECT RSSList.title, RSSList.url, RSSList.interval, RSSList.lastupdatetime, RSSList.id, RSSList.lasterrortime, RSSList.forceupdate, RSSList.errorcount, chatList.config FROM chatList INNER JOIN RSSList ON RSSList.id = chatList.id WHERE chatList.chatId = ? AND chatlist.id = ?;', (chatId, id)) @@ -307,7 +336,7 @@ class database: RSSEntries.append(rssEntry) return RSSEntries - def getUserStatus(self, userId: int) -> (userStatus, str): + def getUserStatus(self, userId: int) -> Tuple[userStatus, str]: with self._value_lock: try: cur = self._db.execute( @@ -327,6 +356,15 @@ class database: except: return False + def removeFromBlackList(self, userId: int): + with self._value_lock: + try: + self._db.execute('DELETE FROM userBlackList WHERE userId=?;', (userId,)) + self._db.commit() + return True + except Exception: + return False + def removeItemInChatList(self, chatId: int, id: int): with self._value_lock: try: diff --git a/rssbot.py b/rssbot.py index 3d712ac..fafc533 100644 --- a/rssbot.py +++ b/rssbot.py @@ -36,10 +36,10 @@ import sys from fileEntry import FileEntries, remove from dictdeal import json2data from rssbotlib import loadRSSBotLib, AddVideoInfoResult -from time import sleep +from time import sleep, time from miraiDatabase import MiraiDatabase from mirai import Mirai -from blackList import BlackList +from blackList import BlackList, InlineKeyBoardForBlackList, getInlineKeyBoardForBlackList, getTextContentForBlackInfo, getInlineKeyBoardForBlackInfo, getTextContentForUnbanBlackInfo, getInlineKeyBoardForUnbanBlackInfo, BlackInfo MAX_ITEM_IN_MEDIA_GROUP = 10 @@ -702,7 +702,8 @@ class messageHandle(Thread): if self._chatId is None: print('未知的chat id') return -1 - if self._main._blackList.isInBlackList(self._chatId): + self._fromUserId = self.__getFromUserId() + if self._main._blackList.isInBlackList(self._chatId) and not self._main._setting.botOwnerList.isOwner(self._fromUserId): c = self.__getChatType() if c == 'private': c = '您' @@ -717,7 +718,6 @@ class messageHandle(Thread): if 'text' in self._data: if 'entities' in self._data: self._botCommand = self.__getBotCommand() - self._fromUserId = self.__getFromUserId() if self._fromUserId is not None: di = {'chat_id': self._chatId} if self.__getChatType() in ['supergroup', 'group'] and self._fromUserId is not None: @@ -852,7 +852,7 @@ class messageHandle(Thread): return if self._botCommand is None and self._data['chat']['type'] in ['group', 'supergroup']: return - if self._botCommand is None or self._botCommand not in ['/help', '/rss', '/rsslist']: + if self._botCommand is None or self._botCommand not in ['/help', '/rss', '/rsslist', '/ban', '/banlist', '/unban']: self._botCommand = '/help' di = {'chat_id': self._chatId} if self.__getChatType() in ['supergroup', 'group'] and self._fromUserId is not None: @@ -860,7 +860,10 @@ class messageHandle(Thread): if self._botCommand == '/help': di['text'] = '''/help 显示帮助 /rss url 订阅RSS -/rsslist [chatId] 获取RSS订阅列表''' +/rsslist [chatId] 获取RSS订阅列表 +/ban 封禁某用户 +/banlist 查询被封禁列表 +/unban 取消封禁某用户''' elif self._botCommand == '/rss': self._botCommandPara = self._getCommandlinePara() self._uri = None @@ -893,6 +896,94 @@ class messageHandle(Thread): else: di['text'] = '正在确认操作者权限……' self._needCheckUser = True + elif self._botCommand == '/banlist': + if self._fromUserId is None or not self._main._setting.botOwnerList.isOwner(self._fromUserId): + di['text'] = '❌你没有权限操作,请与Bot主人进行PY交易以获得权限。' + else: + di['text'] = '列表如下:' + di['reply_markup'] = getInlineKeyBoardForBlackList(self._main._blackList.getBlackList()) + elif self._botCommand in ['/ban', '/unban']: + isban = self._botCommand == '/ban' + words = '封禁' if isban else '取消封禁' + if self._fromUserId is None or not self._main._setting.botOwnerList.isOwner(self._fromUserId): + di['text'] = '❌你没有权限操作,请与Bot主人进行PY交易以获得权限。' + else: + ban_uid: int = None + title: str = None + if 'reply_to_message' in self._data and self._data['reply_to_message'] is not None: + try: + ban_uid = self._data['reply_to_message']['from']['id'] + if self._data['reply_to_message']['from']['is_bot']: + di['text'] = f'尝试{words}Bot,你的请求被滥权了。' + self._main._request('sendMessage', 'post', json=di) + return + title = self._data['reply_to_message']['from']['first_name'] + if self._data['reply_to_message']['from']['last_name'] is not None: + title += f" {self._data['reply_to_message']['from']['last_name']}" + if self._data['reply_to_message']['from']['username'] is not None: + title += f" ({self._data['reply_to_message']['from']['username']})" + except Exception: + pass + self._botCommandPara = self._getCommandlinePara() + first_uid = False + if ban_uid is None: + first_uid = True + if self._botCommandPara[0] in ['chat', 'channel', 'group']: + try: + ban_uid = self._data['chat']['id'] + title = self._data['chat']['title'] + except Exception: + pass + else: + try: + ban_uid = int(self._botCommandPara[0]) + except Exception: + pass + if ban_uid is None: + di['text'] = f'未找到要{words}的用户。' + elif self._main._setting.botOwnerList.isOwner(ban_uid): + di['text'] = f'尝试{words}Bot主人,你的请求被滥权了。' + else: + bl = self._main._blackList.getBlackList() + ind = bl.find(ban_uid) + if ind > -1: + if isban: + di['text'] = '该用户已被封禁。' + self._main._request('sendMessage', 'post', json=di) + return + if bl[ind].from_config: + di['text'] = '无法取消封禁来自配置文件的用户,请修改配置文件后重启bot。' + self._main._request('sendMessage', 'post', json=di) + return + re = self._main._blackList.unban(bl[ind].uid) + if title is None or title == '': + title = str(ban_uid) + link = '' if ban_uid < 0 else f'tg://user?id={ban_uid}' + if re: + di['text'] = f'取消封禁{title}成功!' + else: + di['text'] = f'取消封禁{title}失败!' + di['parse_mode'] = 'HTML' + else: + if not isban: + di['text'] = '该用户未被封禁' + self._main._request('sendMessage', 'post', json=di) + return + reason = '' + if first_uid and len(self._botCommandPara) > 1: + reason = self._botCommandPara[1] + elif not first_uid and len(self._botCommandPara) > 0: + reason = self._botCommandPara[0] + info = BlackInfo(ban_uid, self._fromUserId, int(time()), reason, title) + re = self._main._blackList.ban(info) + if title is None or title == '': + title = str(ban_uid) + link = '' if ban_uid < 0 else f'tg://user?id={ban_uid}' + if re: + di['text'] = f'封禁{title}成功!封禁理由:{reason}' + else: + di['text'] = f'封禁{title}失败!' + di['parse_mode'] = 'HTML' re = self._main._request('sendMessage', 'post', json=di) if self._botCommand == '/rss' and self._uri is not None and re is not None and 'ok' in re and re['ok']: re = re['result'] @@ -969,13 +1060,13 @@ class callbackQueryHandle(Thread): self.answer('您已被封禁。') return l = self._data['data'].split(',') - if len(l) < 3: - self.answer('错误的按钮数据。') - return self._inputList = l try: self._loc = int(l[0]) if self._loc == 0: + if len(l) < 3: + self.answer('错误的按钮数据。') + return self._hashd = l[1] self._command = int(l[2]) except: @@ -1385,6 +1476,96 @@ class callbackQueryHandle(Thread): chatId, rss, ind, self._main._setting.botOwnerList.isOwner(self._fromUserId)) self._main._request("editMessageText", "post", json=di) return + elif self._loc == 2: + if 'message' not in self._data: + self.answer('找不到信息。') + return + if self._fromUserId is None or not self._main._setting.botOwnerList.isOwner(self._fromUserId): + self.answer('❌你没有权限操作,请与Bot主人进行PY交易以获得权限。') + return + try: + self._inlineKeyBoardForBlackListCommand = InlineKeyBoardForBlackList(int(self._inputList[1])) + except Exception: + self.answer('未知的按钮。') + return + di = {'chat_id': self._data['message']['chat']['id'], + 'message_id': self._data['message']['message_id']} + if self._inlineKeyBoardForBlackListCommand == InlineKeyBoardForBlackList.FirstPage: + di['text'] = '列表如下:' + di['reply_markup'] = getInlineKeyBoardForBlackList(self._main._blackList.getBlackList()) + self._main._request("editMessageText", "post", json=di) + return + elif self._inlineKeyBoardForBlackListCommand == InlineKeyBoardForBlackList.LastPage: + di['text'] = '列表如下:' + di['reply_markup'] = getInlineKeyBoardForBlackList(self._main._blackList.getBlackList(), lastPage=True) + self._main._request("editMessageText", "post", json=di) + return + elif self._inlineKeyBoardForBlackListCommand == InlineKeyBoardForBlackList.PrevPage: + di['text'] = '列表如下:' + di['reply_markup'] = getInlineKeyBoardForBlackList(self._main._blackList.getBlackList(), int(self._inputList[2]) - 1) + self._main._request("editMessageText", "post", json=di) + return + elif self._inlineKeyBoardForBlackListCommand == InlineKeyBoardForBlackList.NextPage: + di['text'] = '列表如下:' + di['reply_markup'] = getInlineKeyBoardForBlackList(self._main._blackList.getBlackList(), int(self._inputList[2]) + 1) + self._main._request("editMessageText", "post", json=di) + return + elif self._inlineKeyBoardForBlackListCommand == InlineKeyBoardForBlackList.Close: + self._main._request("deleteMessage", "post", json=di) + return + elif self._inlineKeyBoardForBlackListCommand in [InlineKeyBoardForBlackList.BlackInfo, InlineKeyBoardForBlackList.CancleUnban]: + bl = self._main._blackList.getBlackList() + ind = bl.find(int(self._inputList[3])) + if ind == -1: + self.answer('在黑名单里找不到该用户。') + di['text'] = '列表如下:' + di['reply_markup'] = getInlineKeyBoardForBlackList(bl, itemIndex=int(self._inputList[2])) + self._main._request("editMessageText", "post", json=di) + return + else: + di['text'] = getTextContentForBlackInfo(bl[ind]) + di['parse_mode'] = 'HTML' + di['reply_markup'] = getInlineKeyBoardForBlackInfo(bl[ind], ind) + self._main._request("editMessageText", "post", json=di) + return + elif self._inlineKeyBoardForBlackListCommand == InlineKeyBoardForBlackList.BackToList: + di['text'] = '列表如下:' + di['reply_markup'] = getInlineKeyBoardForBlackList(self._main._blackList.getBlackList(), itemIndex=int(self._inputList[2])) + self._main._request("editMessageText", "post", json=di) + return + elif self._inlineKeyBoardForBlackListCommand == InlineKeyBoardForBlackList.Unban: + bl = self._main._blackList.getBlackList() + ind = bl.find(int(self._inputList[3])) + if ind == -1: + self.answer('在黑名单里找不到该用户。') + di['text'] = '列表如下:' + di['reply_markup'] = getInlineKeyBoardForBlackList(bl, itemIndex=int(self._inputList[2])) + self._main._request("editMessageText", "post", json=di) + return + else: + if bl[ind].from_config: + self.answer('无法取消封禁来自配置文件的用户,请修改配置文件后重启bot。') + return + di['text'] = getTextContentForUnbanBlackInfo(bl[ind]) + di['parse_mode'] = 'HTML' + di['reply_markup'] = getInlineKeyBoardForUnbanBlackInfo(bl[ind], ind) + self._main._request("editMessageText", "post", json=di) + return + elif self._inlineKeyBoardForBlackListCommand == InlineKeyBoardForBlackList.ConfirmUnban: + bl = self._main._blackList.getBlackList() + ind = bl.find(int(self._inputList[3])) + if ind == -1: + self.answer('在黑名单里找不到该用户。') + else: + if bl[ind].from_config: + self.answer('无法取消封禁来自配置文件的用户,请修改配置文件后重启bot。') + return + re = self._main._blackList.unban(bl[ind].uid) + self.answer('取消封禁' + ('成功!' if re else '失败!')) + di['text'] = '列表如下:' + di['reply_markup'] = getInlineKeyBoardForBlackList(self._main._blackList.getBlackList(), itemIndex=int(self._inputList[2])) + self._main._request("editMessageText", "post", json=di) + return else: self.answer('未知的按钮。') return diff --git a/textc.py b/textc.py index 2e96ffe..47c7422 100644 --- a/textc.py +++ b/textc.py @@ -162,6 +162,10 @@ class textc: p.close() return len(p.s) + def __iadd__(self, i): + self.addtotext(i) + return self + def __init__(self): self.__str = '' self.__max = 4096