def event_set(bot_id, group_id, user_id, message, key, value, **argv): MessageLogs.add(group_id, user_id, nAIset=1) #紀錄次數 if group_id is None: return '目前沒有個人設定喵' else: if key == '全群組關鍵字': UserSettings.update(group_id, all_group_keyword=str2bool(value)) return '設定完成' elif key == '髒話過濾': UserSettings.update(group_id, filter_fuck=str2bool(value)) else: return ('目前可使用:\n' '設定=髒話過濾=開/關\n' '設定=全群組關鍵字=開/閉\n(可以使用別的群組的關鍵字)\n')
def event_add(bot_id, group_id, user_id, message, key, value, **argv): MessageLogs.add(group_id, user_id, nAIset=1) #紀錄次數 filter_fuck = (group_id is None or UserSettings.get(group_id, 'filter_fuck', True)) if filter_fuck and isValueHaveKeys(message, cfg['詞組']['髒話']): return '愛醬覺得說髒話是不對的!!' if key is None: return cfg['學習說明'] key = key.lower() if value is None or value == '': reply_message = ['<%s>' % key] if group_id is not None: data = UserKeyword.get(group_id, key) if data is not None: reply_message.append('群組=%s' % data.reply) if user_id is not None: data = UserKeyword.get(user_id, key) if data is not None: reply_message.append('個人=%s' % data.reply) return '\n'.join(reply_message) if len( reply_message) > 1 else '喵喵喵? 愛醬不記得<%s>' % (key) while '***' in key: key = key.replace('***', '**') while '|||' in key: key = key.replace('|||', '||') while '___' in key: key = key.replace('___', '__') for i in value.replace('__', '||').split('||'): i = i.strip() if i[:4] == 'http' and not is_image_and_ready(i): return '<%s>\n愛醬發現圖片網址是錯誤的\n請使用格式(jpg, png)\n短網址或網頁嵌圖片可能無效\n必須使用https' % i break #如果全部都檢查時間會太久 只幫第一個檢查格式 通常使用者圖床也會使用同一個 應該不會有問題 reply_message = ['<%s>記住了喔 ' % key] if group_id is not None and UserKeyword.add_and_update( group_id, user_id, key, value): reply_message.append('(群組)') if user_id is not None and UserKeyword.add_and_update( user_id, user_id, key, value): reply_message.append('(個人)') else: reply_message.append('(不儲存個人)\nhttps://goo.gl/zTwaL2') return ''.join(reply_message)
def event_text_main(bot_id, group_id, user_id, message, reply_token, **argv): #紀錄最後使用時間 用來移除過久無人使用的資料 if group_id is not None: UserSettings.refresh_last_time(group_id) if user_id is not None: UserSettings.refresh_last_time(user_id) message = message.replace('"', '"').replace("'", '’').replace( ';', ';') #替換一些字元成全型 防SQL注入攻擊 order, *value = message.split('=') for key, func in event_funcs.items(): if key == order or key == '': reply_message = func( bot_id=bot_id, group_id=group_id, user_id=user_id, message=message, key=(value[0] if len(value) > 0 else None), value=('='.join(value[1:]) if len(value) > 1 else None), **argv) #公告 if reply_message is None: reply_message = [] if type(reply_message) == str: reply_message = [reply_message] if group_id is not None and UserSettings.check_news(group_id): reply_message.append(''.join( [cfg['公告']['ver'], ' ', cfg['公告']['內容']])) for mid in [user_id, group_id]: if mid is not None and UserSettings_temp.has_option( mid, '臨時公告'): reply_message.append(UserSettings_temp.get(mid, '臨時公告')) UserSettings_temp.remove_option(mid, '臨時公告') UserSettings_temp.save() bots[bot_id].reply_message(reply_token, reply_message) db.session.commit() return reply_message
def check(self, userkeyword_list, all_reply=False): ''' 關鍵字觸發的邏輯檢查 ''' exclude_url = all_reply and not (not self.group or UserSettings.get( self.group.id, None, '全圖片', default=False)) keys = [] result = [] for row in userkeyword_list: row_reply = row.reply if row_reply[:1] == '@': #全回應模式過濾開頭@ if all_reply: continue else: row_reply = row.reply[1:] if exclude_url and 'https:' in row_reply: #全回應模式過濾網址只排除可能是圖片類型的 continue if row.keyword == self.message: if all_reply: result.append(row_reply) else: return self.later(row_reply) elif row.keyword.replace('**', '') == self.message: result.append(row_reply) elif not all_reply or len(row_reply) > 1: #超過一個字才加入全回應 keys.append((row.keyword, row_reply)) if len(result) > 0: return self.later(choice(result)) #結果集隨機抽取一個 results = {} result_level = -99 for k, v in keys: try: kn = -1 k_arr = k.split('**') for k2 in k_arr: if k2 != '': n = self.message.find(k2) if n > kn: kn = n else: break #最後檢查前後如果為任意字元的情況 那相對的最前最後一個字元必須相等 雖然使用字串會比較精準 暫時先用一個字元 如果**混在中間有可能誤判 但是問題不大 if k_arr[0] != '' and self.message[0] != k[0]: break if k_arr[-1] != '' and self.message[-1] != k[-1]: break else: #result.append(v) level = len(k) - k.count('**') - (2 if k[:2] == '**' else 0) - (2 if k[-2:] == '**' else 0) if not level in results: results[level] = [] if level > result_level: result_level = level results[level].append(v) except Exception as e: bots['admin'].send_message( cfg['admin_line'], '錯誤:%s\ngid:%s\nuid:%s\nmsg:%s\nkey:<%s>\n<%s>' % (str(e), self.group_id, self.user_id, self.message, k, v)) raise e if len(results) > 0: return self.later(choice(results[result_level])) return None
def main(self): ''' 關鍵字觸發 ''' if 'http:' in self.message or 'https:' in self.message: #如果內容含有網址 做網址檢查 if self.group: self._count({'網頁': 1}) #紀錄次數 return google_safe_browsing(self.message) else: return google_shorten_url(self.message) #短網址 self.message = self.message.lower().strip(' \n') #調整內容 以增加觸發命中率 self._count({ '對話': 1, '髒話': self.message.count('幹') + self.message.count('f**k'), '字數': len(self.message), }) if self.message == '': return None #暫存訊息 用於對話模型訓練 #if self.group: # with open('E:\\bot_log\\%s.log' % self.group_id, 'a+', encoding='utf-8') as f: # f.write(self.message + '\n') #愛醬開頭可以強制呼叫 if self.group: if self.message != text['名稱'] and self.message[:2] == text['名稱']: message_old = self.message self.message = self.message[2:].strip(' \n ') reply_message = self.check(UserKeyword.get(self.group_id)) if reply_message: return reply_message reply_message = self.check(UserKeyword.get(self.user_id)) if reply_message: return reply_message self.message = message_old #睡覺模式 if UserSettings_temp.has_option(self.group_id, '暫停'): if time() > UserSettings_temp.getfloat(self.group_id, '暫停'): UserSettings_temp.remove_option(self.group_id, '暫停') UserSettings_temp.save() return text['睡醒'] #一般模式 else: if not self.group or (self.user and UserSettings.get( self.group.id, self.user.id, '個人詞庫', False)): #檢查是否使用個人詞庫 reply_message = self.check(UserKeyword.get(self.user.id)) if reply_message: return reply_message if self.group and self.user: if not UserSettings.get(self.group.id, self.user.id, '別理我', False): #檢查不理我模式 reply_message = self.check(UserKeyword.get(self.group.id)) if reply_message: return reply_message #全回應模式 if self.message[:2] == text['名稱'] or self.group_id is None: if self.group is None or UserSettings.get( self.group_id, None, '全回應', default=False): if self.message != text['名稱'] and self.message[:2] == text[ '名稱']: #做兩層是為了方便1對1不見得也要愛醬開頭 self.message = self.message[2:].strip(' \n ') reply_message = self.check(UserKeyword.get(), all_reply=True) if reply_message: return reply_message if self.group: return choice(text['未知']) else: return choice(text['未知']) + '\n(全回應模式關閉)\n使用「設定」開啟' if self.group_id is None: return text['預設回覆'] else: return None
def settings(self): ''' 設定 ''' if self.key is None: return [ '設定=別理我=開/關\n' '設定=個人詞庫=開/關\n' '設定=全回應=開/關\n' '設定=全圖片=開/關(需要全回應)\n' '設定=幫忙愛醬=開/關\n' '\n' '(不輸入值可查看說明)', UserSettings.show(self.group_id, self.user_id) ] try: if self.key in ['過濾髒話', '髒話過濾']: return '這設定已經移除了' #全群組 if self.key == '全回應': if self.value is None: return '開啟後愛醬開頭的對話將從全部的詞庫中產生反應\n「預設:關」' UserSettings.update(self.group_id, None, {'全回應': text2bool(self.value)}) return '設定完成' if self.key == '全圖片': if self.value is None: return '開啟後全回應的結果包含圖片\n(需要開啟全圖片)\n(注意:圖片沒有任何審核 有可能出現不適圖片 如可接受再開啟)\n「預設:關」' UserSettings.update(self.group_id, None, {'全圖片': text2bool(self.value)}) return '設定完成' if self.key == '幫忙愛醬': if self.value is None: return '開啟後會快取群組的對話紀錄\n只用於機器學習\n作者會稍微過目後進行分類丟入程式\n用完後刪除「預設:關」\n使用「設定=幫忙愛醬=開」開啟' UserSettings.update(self.group_id, None, {'幫忙愛醬': text2bool(self.value)}) return '設定完成' #群組中個人 if self.key == '別理我': if self.value is None: return '開啟後愛醬不會在此群組對你產生回應\n(愛醬開頭還是可以強制呼叫)\n「預設:關」' if self.user_id is None: return text['權限不足'] UserSettings.update(self.group_id, self.user_id, {'別理我': text2bool(self.value)}) return '設定完成' if self.key == '個人詞庫': if self.value is None: return '開啟後會對你的個人詞庫產生回應\n「預設:關」' if self.user_id is None: return text['權限不足'] UserSettings.update(self.group_id, self.user_id, {'個人詞庫': text2bool(self.value)}) return '設定完成' return '沒有此設定喔' except Exception as e: return '設定錯誤 <%s>' % str(e)
def later(self, reply_message): ''' 關鍵字觸發的後處理 ''' self._count({'觸發':1}) #取參數 opt = {} if '##' in reply_message: reply_message_new = [] for i in reply_message.split('##'): if '=' in i: a, *b = i.split('=') opt[a] = '='.join(b) else: reply_message_new.append(i) #reply_message = ''.join(reply_message_new) reply_message = reply_message[:reply_message.find('##')] #參數之後會由add儲存至database 這邊之後會廢棄 filter_fuck = self.group_id is not None and UserSettings.get(self.group_id, None, '髒話過濾', True) if filter_fuck and isValueHaveKeys(self.message, cfg['詞組']['髒話']): return '愛醬是好孩子不說髒話!!\n(可用「設定」關閉)' #隨機 (算法:比重) if '__' in reply_message: weight_total = 0 result_pool = {} minimum_pool = [] for msg in reply_message.split('__'): if msg == '': continue index = msg.rfind('%') if index > -1 and isFloat(msg[index+1:].strip()): #if index > -1 and msg[index+1:].strip().isdigit(): weight = float(msg[index+1:].strip()) msg = msg[:index] else: weight = 1 weight_total += weight is_minimum = msg[:1] == '*' if is_minimum: is_minimum_pool = msg[:2] == '**' msg = msg[2:] if is_minimum_pool else msg[1:] result_pool[msg] = { 'weight':weight, 'is_minimum':is_minimum, } if is_minimum and is_minimum_pool: minimum_pool.append(msg) if opt.get('百分比', '0').isdigit(): #百分比隨機模式 number = int(opt.get('百分比', '0')) if number > 0: reply_message = [] total = 100.0 if number > len(result_pool): number = len(result_pool) result_pool = sample([msg for msg, msg_opt in result_pool.items()], number) n = 0 for msg in result_pool: n += 1 if n >= number or n >= len(result_pool): ratio = total total = 0 else: ratio = uniform(0, total) total -= ratio reply_message.append('%s(%3.2f%)' % (msg, ratio)) if total <= 0: break return '\n'.join(reply_message) count = int(self.message[self.message.rfind('*')+1:]) if '*' in self.message and self.message[self.message.rfind('*')+1:].isdigit() else 1 if count > 10000: count = 10000 if count < 1: count = 1 if count == 1 and '種子' in opt and opt['種子'].isdigit() and int(opt['種子']) > 0: seed_time = int((datetime.now()-datetime(2017,1,1)).days * 24 / int(opt['種子'])) seed = int(md5((str(self.user_id) + str(seed_time)).encode()).hexdigest().encode(), 16) % weight_total else: try: #random.org的隨機據說為真隨機 if count > 1: r = requests.get('https://www.random.org/integers/?num=%s&min=0&max=%s&col=1&base=10&format=plain&rnd=new' % (count, int(weight_total)), timeout=3) if 'Error' in r.text: raise seed = r.text.split('\n')[:-1] else: raise except: seed = [uniform(0, int(weight_total)) for i in range(count)] minimum_count = 0 minimum_index = int(opt.get('保底', 10)) reply_message_new = {} reply_message_image = [] for i in range(count): #r = uniform(0, weight_total) if seed == -1 else seed r = float(seed[i]) if type(seed) == list else seed for msg, msg_opt in result_pool.items(): if r > msg_opt['weight']: r -= msg_opt['weight'] else: minimum_count = 0 if msg_opt['is_minimum'] else minimum_count + 1 if minimum_count >= minimum_index and len(minimum_pool) > 0: minimum_count = 0 msg = choice(minimum_pool) if msg[:6] == 'https:': reply_message_image.append(msg) if len(reply_message_image) > 5: break else: reply_message_new[msg] = (reply_message_new[msg] + 1) if msg in reply_message_new else 1 break if len(reply_message_new) > 0: if count == 1: reply_message = list(reply_message_new.keys()) else: reply_message = [] for msg, num in reply_message_new.items(): reply_message.append('%s x %s' % (msg, num)) reply_message = ['\n'.join(reply_message)] else: reply_message = [] reply_message.extend(reply_message_image[:5]) #這邊有待優化 if type(reply_message) == str: reply_message = [reply_message] reply_message_new = [] for msg in reply_message: for msg_split in msg.split('||'): reply_message_new.append(msg_split) return reply_message_new
def event_main(bot_id, group_id, user_id, message, key, value, **argv): #後處理 def later(reply_message): MessageLogs.add(group_id, user_id, nAItrigger=1) #紀錄次數 #取參數 opt = {} if '##' in reply_message: reply_message_new = [] for i in reply_message.split('##'): if '=' in i: a, *b = i.split('=') opt[a] = '='.join(b) else: reply_message_new.append(i) reply_message = ''.join(reply_message_new) #隨機 (算法:比重) if '__' in reply_message: weight_total = 0 result_pool = {} minimum_pool = [] for msg in reply_message.split('__'): if msg == '': continue index = msg.rfind('%') if index > -1 and isFloat(msg[index + 1:].strip()): #if index > -1 and msg[index+1:].strip().isdigit(): weight = float(msg[index + 1:].strip()) msg = msg[:index] else: weight = 1 weight_total += weight is_minimum = msg[:1] == '*' if is_minimum: is_minimum_pool = msg[:2] == '**' msg = msg[2:] if is_minimum_pool else msg[1:] result_pool[msg] = { 'weight': weight, 'is_minimum': is_minimum, } if is_minimum and is_minimum_pool: minimum_pool.append(msg) count = int( message[message.rfind('*') + 1:]) if '*' in message and message[message.rfind('*') + 1:].isdigit() else 1 if count > 10000: count = 10000 if count < 1: count = 1 if count == 1 and '種子' in opt and opt['種子'].isdigit() and int( opt['種子']) > 0: seed_time = int((datetime.now() - datetime(2017, 1, 1)).days * 24 / int(opt['種子'])) seed = int( md5((str(user_id) + str(seed_time) ).encode()).hexdigest().encode(), 16) % weight_total else: try: #random.org的隨機據說為真隨機 if count > 1: r = requests.get( 'https://www.random.org/integers/?num=%s&min=0&max=%s&col=1&base=10&format=plain&rnd=new' % (count, int(weight_total)), timeout=3) if 'Error' in r.text: raise seed = r.text.split('\n')[:-1] else: raise except: seed = [ uniform(0, int(weight_total)) for i in range(count) ] minimum_count = 0 minimum_index = int(opt.get('保底', 10)) reply_message_new = {} reply_message_image = [] for i in range(count): #r = uniform(0, weight_total) if seed == -1 else seed r = float(seed[i]) if type(seed) == list else seed for msg, msg_opt in result_pool.items(): if r > msg_opt['weight']: r -= msg_opt['weight'] else: minimum_count = 0 if msg_opt[ 'is_minimum'] else minimum_count + 1 if minimum_count >= minimum_index and len( minimum_pool) > 0: minimum_count = 0 msg = choice(minimum_pool) if msg[:6] == 'https:': reply_message_image.append(msg) if len(reply_message_image) > 5: break else: reply_message_new[msg] = ( reply_message_new[msg] + 1) if msg in reply_message_new else 1 break if len(reply_message_new) > 0: if count == 1: reply_message = list(reply_message_new.keys()) else: reply_message = [] for msg, num in reply_message_new.items(): reply_message.append('%s x %s' % (msg, num)) reply_message = ['\n'.join(reply_message)] else: reply_message = [] reply_message.extend(reply_message_image[:5]) #這邊有待優化 if type(reply_message) == str: reply_message = [reply_message] reply_message_new = [] for msg in reply_message: for msg_split in msg.split('||'): reply_message_new.append(msg_split) return reply_message_new message = message.lower() if 'http:' in message or 'https:' in message: #如果內容含有網址 不觸發 順便紀錄 MessageLogs.add(group_id, user_id, nUrl=1) #紀錄次數 return None else: MessageLogs.add(group_id, user_id, nText=1, nFuck=(message.count('幹') + message.count('f**k')), nLenght=len(message)) #紀錄次數 if UserSettings_temp.has_option(group_id, '暫停'): if time() > UserSettings_temp.getfloat(group_id, '暫停'): UserSettings_temp.remove_option(group_id, '暫停') UserSettings_temp.save() return '愛醬大復活!' else: return None if group_id is not None: reply_message = UserKeyword.get(group_id, message) if reply_message is not None: return later(reply_message.reply) if user_id is not None: reply_message = UserKeyword.get(user_id, message) if reply_message is not None: return later(reply_message.reply) keys = [] if group_id is not None: for i in UserKeyword.get(group_id): keys.append((i.keyword, i.reply)) if user_id is not None: for i in UserKeyword.get(user_id): keys.append((i.keyword, i.reply)) for k, v in keys: kn = -1 k_arr = k.split('**') for k2 in k_arr: if k2 != '': n = message.find(k2) if n > kn: kn = n else: break #最後檢查前後如果為任意字元的情況 那相對的最前最後一個字元必須相等 雖然使用字串會比較精準 暫時先用一個字元 如果**混在中間有可能誤判 但是問題不大 if k_arr[0] != '' and message[0] != k[0]: break if k_arr[-1] != '' and message[-1] != k[-1]: break else: return later(v) #使用全群組的關鍵字 限無** if group_id is not None and UserSettings.get(group_id, 'all_group_keyword', False): for k in UserKeyword.query.filter_by( super=0, keyword=message).order_by(UserKeyword._id.desc()): return later(k.reply) if group_id is None: message = message.strip() if message[:4] == 'http': return '愛醬幫你申請短網址了喵\n%s' % google_shorten_url(message) else: return '群組指令說明輸入【指令】\n個人服務:\n直接傳給愛醬網址即可產生短網址\n直接傳圖給愛醬即可上傳到圖床\n其他功能如果有建議請使用回報' else: return None