bot.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. from bot_settings import *
  2. from some_data import *
  3. from twitchio.ext import commands, routines
  4. from twitchio.user import User
  5. from twitchio.channel import Channel
  6. from db_client import db_message_log_client
  7. import datetime
  8. import calendar
  9. from dateutil import tz
  10. import random
  11. import re
  12. import requests
  13. from bot_utilities import *
  14. from gpiozero import CPUTemperature
  15. class twitch_bot(commands.Bot):
  16. name = str()
  17. dbLogClient = db_message_log_client(DB_CONNECTION_STRING)
  18. msg_titles = ['сообщение', 'сообщения', 'сообщений']
  19. #Инициализация бота
  20. def __init__(self, name):
  21. super().__init__(token=ACCESS_TOKEN, prefix=PREFIX, initial_channels=INITIAL_CHANNELS)
  22. self.dbLogClient.connect()
  23. #Событие готовности бота
  24. async def event_ready(self):
  25. print(f'Вошел как | {self.nick}')
  26. print(f'Id пользователя | {self.user_id}')
  27. #Обработка сообщений
  28. async def event_message(self, message):
  29. if message.echo:
  30. author_id = self.user_id
  31. author_name = self.nick
  32. else:
  33. author_id = message.author.id
  34. author_name = message.author.name
  35. utc_time = message.timestamp
  36. utc_time = utc_time.replace(tzinfo=tz.tzutc())
  37. localTime = utc_time.astimezone(tz.tzlocal())
  38. print(f'{localTime}({message.channel.name}){author_name}:{message.content}')
  39. channel_user = await message.channel.user(False)
  40. self.dbLogClient.insert_message(message.content, author_id, author_name, channel_user, localTime)
  41. if message.echo:
  42. return
  43. if str(message.content).startswith(PREFIX):
  44. await self.handle_commands(message)
  45. return
  46. #Опускание мубота
  47. if message.author.name == 'moobot':
  48. await message.channel.send(f'@{message.author.name}, мубот соси')
  49. return
  50. #Приветствия и покатствия
  51. check_str = re.split(r',|!|;|\.|\?', message.content)[0]
  52. if check_str in hellos:
  53. await message.channel.send(f'@{message.author.name}, {random.choice(hellos)}')
  54. return
  55. if check_str in byes:
  56. await message.channel.send(f'@{message.author.name}, {random.choice(byes)}')
  57. return
  58. if check_str in custom_copypast_cmd and message.channel.name in ALLOW_FLOOD:
  59. await message.channel.send(check_str)
  60. return
  61. if(str(f'@{self.nick}') in str(message.content).lower()):
  62. channel_user = await message.channel.user()
  63. await message.channel.send(f'@{message.author.name}, {self.dbLogClient.get_random_message_by_user(channel_user.id)}')
  64. #await message.channel.send(f'@{message.author.name}, {self.dbLogClient.GetRandomMessageByUser(40348923)}')
  65. return
  66. #Информационные команды
  67. @commands.command(name='тг', aliases=['телеграм', 'телега', 'telegram', 'tg'])
  68. async def telegram(self, ctx: commands.Context):
  69. msg = telegrams.get(ctx.channel.name)
  70. if(not msg == None):
  71. await ctx.send(msg)
  72. @commands.command(name='вконтакте', aliases=['вк', 'vk', 'vkontakte'])
  73. async def vkontakte(self, ctx: commands.Context):
  74. msg = vks.get(ctx.channel.name)
  75. if(not msg == None):
  76. await ctx.send(msg)
  77. @commands.command(name='бусти', aliases=['boosty', 'кошка'])
  78. async def boosty(self, ctx: commands.Context):
  79. msg = boostys.get(ctx.channel.name)
  80. if(not msg == None):
  81. await ctx.send(msg)
  82. @commands.command(name='донат', aliases=['donat', 'пожертвование'])
  83. async def donat(self, ctx: commands.Context):
  84. msg = donats.get(ctx.channel.name)
  85. if(not msg == None):
  86. await ctx.send(msg)
  87. @commands.command(name='мем', aliases=['меме', 'meme'])
  88. async def meme(self, ctx: commands.Context):
  89. msg = memes.get(ctx.channel.name)
  90. if(not msg == None):
  91. await ctx.send(msg)
  92. @commands.command(name='смайлы', aliases=['7tv', 'smiles', 'emoji', 'смайлики', 'эмоуты'])
  93. async def SpecialSmiles(self, ctx: commands.Context):
  94. if ctx.channel.name in ALLOW_URL:
  95. await ctx.send('Чтобы видеть и посылать крутые смайлы в чате устанавливайте расширение для браузера по ссылке: https://7tv.app/')
  96. @commands.command(name='help', aliases=['commands', 'команды', 'помощь', 'бот'])
  97. async def help_bot(self, ctx: commands.Context):
  98. await ctx.send(f'@{ctx.author.name} Я бот и я ничего не умею 4Head')
  99. @commands.cooldown(rate=1, per=60, bucket=commands.Bucket.channel)
  100. @commands.command(name='lastseen', aliases=['когдавидели'])
  101. async def last_seen(self, ctx: commands.Context):
  102. channel_user = await ctx.channel.user()
  103. last_activity = self.dbLogClient.get_channel_author_last_activity(channel_user.id)
  104. if (last_activity):
  105. await ctx.send(f'@{ctx.author.name}, в последний раз {ctx.channel.name} видели в чате {last_activity.strftime("%d.%m.%Y в %H:%M:%S")} CoolStoryBob');
  106. else:
  107. await ctx.send(f'@{ctx.author.name}, я не помню когда в последний раз видел в чате {ctx.channel.name} PoroSad');
  108. @commands.cooldown(rate=1, per=300, bucket=commands.Bucket.member)
  109. @commands.command(name='followage', aliases=['возрастотслеживания'])
  110. async def followage(self, ctx: commands.Context):
  111. if ctx.author.name == ctx.channel.name:
  112. await ctx.send(f'@{ctx.author.name}, ты не можешь отслеживать сам себя CoolStoryBob')
  113. return
  114. r = requests.get(f'https://api.ivr.fi/v2/twitch/subage/{ctx.author.name}/{ctx.channel.name}')
  115. if r.status_code >= 400:
  116. await ctx.send(f'@{ctx.author.name}, не удалось выполнить запрос времени отслеживания PoroSad')
  117. return
  118. followedAt = r.json()["followedAt"]
  119. if followedAt :
  120. follow_age = datetime.datetime.now() - datetime.datetime.fromisoformat(followedAt.replace('Z',''))
  121. await ctx.send(f'@{ctx.author.name}, ты отслеживаешь канал {ctx.channel.name} {follow_age.days} дней SeemsGood')
  122. else:
  123. await ctx.send(f'@{ctx.author.name}, ты не отслеживаешь канал {ctx.channel.name} D:')
  124. @commands.cooldown(rate=1, per=30, bucket=commands.Bucket.channel)
  125. @commands.command(name='день')
  126. async def whatdaytoday(self, ctx: commands.Context):
  127. await ctx.send(f'@{ctx.author.name}, {get_today_holiday()}')
  128. @commands.cooldown(rate=1, per=30, bucket=commands.Bucket.member)
  129. @commands.command(name='погода', aliases=['weather'])
  130. async def weather(self, ctx: commands.Context):
  131. direct_translate = {
  132. 'N' : 'С',
  133. 'W' : 'З',
  134. 'S' : 'Ю',
  135. 'E' : 'В'
  136. }
  137. url = "https://weatherapi-com.p.rapidapi.com/current.json"
  138. arg = ctx.message.content.rstrip(' ').split(' ', 1)[1]
  139. querystring = {"q":arg,"lang":"ru"}
  140. response = requests.get(url, headers=weather_headers, params=querystring)
  141. if response.status_code < 400:
  142. jsonR = response.json()
  143. await ctx.send(f'@{ctx.author.name}, в {jsonR["location"]["name"]} на данный момент {jsonR["current"]["temp_c"]}°C. {jsonR["current"]["condition"]["text"]}. Ветер {replace_chars(jsonR["current"]["wind_dir"], direct_translate)} {jsonR["current"]["wind_kph"] * 1000 / 3600:.2f} м/с. peepoPls')
  144. else:
  145. await ctx.send(f'@{ctx.author.name}, не удалось выполнить запрос погоды PoroSad')
  146. @commands.cooldown(rate=1, per=30, bucket=commands.Bucket.member)
  147. @commands.command(name='ogeyofday')
  148. async def ogeyofday(self, ctx: commands.Context):
  149. if ctx.channel.name not in OGEY_OF_DAY_CHANNELS:
  150. return
  151. channel_user = await ctx.channel.user()
  152. ogey_name = self.dbLogClient.get_ogey(channel_user.id)
  153. if ogey_name != None:
  154. await ctx.send(f'@{ctx.author.name}, Ogey дня сегодня {ogey_name}, можно только позавидовать этому чатеру EZ Clap')
  155. else:
  156. await ctx.send(f'@{ctx.author.name}, Ogey дня не определен PoroSad')
  157. #Команды под оффлайн чат
  158. @commands.cooldown(rate=1, per=10, bucket=commands.Bucket.member)
  159. @commands.command(name='чмок')
  160. async def chmok(self, ctx: commands.Context, phrase: str | None):
  161. if await self.is_stream_online(ctx.channel):
  162. return
  163. if len(ctx.chatters) == 0:
  164. await ctx.send('В этом чате некого чмокнуть PoroSad')
  165. elif not phrase:
  166. await ctx.send(f'@{ctx.author.name} чмокнул @{random.choice(tuple(ctx.chatters)).name} 😘')
  167. else:
  168. if not is_valid_args(phrase):
  169. await ctx.send(f'@{ctx.author.name}, бана хочешь моего?')
  170. elif ctx.author.name in phrase.lower():
  171. await ctx.send(f'@{ctx.author.name} боюсь что это нереально. Давай лучше я 😘')
  172. elif self.nick in phrase.lower():
  173. await ctx.send(f'@{ctx.author.name}, и тебе чмок 😘')
  174. else:
  175. await ctx.send(f'@{ctx.author.name} чмокнул {phrase} 😘')
  176. @commands.cooldown(rate=1, per=10, bucket=commands.Bucket.member)
  177. @commands.command(name='лапочка')
  178. async def lapochka(self, ctx: commands.Context, phrase: str | None):
  179. if await self.is_stream_online(ctx.channel):
  180. return
  181. if len(ctx.chatters) == 0:
  182. await ctx.send('В этом чате пусто PoroSad')
  183. elif not phrase:
  184. await ctx.send(f'@{ctx.author.name} назвал лапочкой @{random.choice(tuple(ctx.chatters)).name} <3')
  185. else:
  186. if not is_valid_args(phrase):
  187. await ctx.send(f'@{ctx.author.name}, бана хочешь моего?')
  188. elif ctx.author.name in phrase.lower():
  189. await ctx.send(f'@{ctx.author.name} высокая самооценка это хорошо SeemsGood')
  190. elif self.nick in phrase.lower():
  191. await ctx.send(f'@{ctx.author.name}, ой спасибо bleedPurple')
  192. else:
  193. await ctx.send(f'@{ctx.author.name} назвал лапочкой {phrase} <3')
  194. @commands.cooldown(rate=1, per=60, bucket=commands.Bucket.channel)
  195. @commands.command(name='анек', aliases=['кринж'])
  196. async def anek(self, ctx: commands.Context):
  197. await ctx.send(get_rand_anek())
  198. @commands.cooldown(rate=1, per=10, bucket=commands.Bucket.user)
  199. @commands.command(name='ауф', aliases=['auf'])
  200. async def auf(self, ctx: commands.Context):
  201. await ctx.send(random.choice(auf_messages))
  202. @commands.cooldown(rate=1, per=7200, bucket=commands.Bucket.channel)
  203. @commands.command(name='привет', aliases=['hello', 'hi'])
  204. async def hello(self, ctx: commands.Context):
  205. if await self.is_stream_online(ctx.channel):
  206. return
  207. channel_user = await ctx.channel.user()
  208. active_users = self.dbLogClient.get_last_active_users(channel_user.id)
  209. if not active_users:
  210. await ctx.send('Я не знаю кто был в чате недавно. Поэтому привет всем KonCha')
  211. return
  212. msg = f'Привет,'
  213. for user_row in active_users:
  214. msg = f' {msg} @{user_row[0]},'
  215. msg = msg + ' KonCha'
  216. await ctx.send(msg)
  217. @commands.cooldown(rate=1, per=600, bucket=commands.Bucket.channel)
  218. @commands.command(name='топ', aliases=['top'])
  219. async def top(self, ctx: commands.Context):
  220. channel_user = await ctx.channel.user()
  221. top_users = self.dbLogClient.get_top_of_month_users(channel_user.id)
  222. if not top_users:
  223. await ctx.send('Не найдены сообщения для топа NotLikeThis')
  224. return
  225. msg = f'Топ месяца по сообщениям:'
  226. for user_row in top_users:
  227. msg = f' {msg} {user_row[0]}({user_row[1]:,}),'
  228. msg = msg + ' PogChamp'
  229. await ctx.send(msg)
  230. @commands.cooldown(rate=1, per=30, bucket=commands.Bucket.user)
  231. @commands.command(name='скольконасрал')
  232. async def skolkonasral(self, ctx: commands.Context, phrase: str | None):
  233. if await self.is_stream_online(ctx.channel):
  234. return
  235. channel_user = await ctx.channel.user()
  236. if phrase:
  237. name = phrase
  238. else:
  239. name = ctx.author.name
  240. msg_count = self.dbLogClient.get_users_message_count_for_mounth_by_name(channel_user.id, name.lower())
  241. if not msg_count:
  242. await ctx.send(f'@{ctx.author.name}, не удалось подсчитать сообщения запрошеного пользователя NotLikeThis.')
  243. return
  244. await ctx.send(f"В этом месяце {name} написал в чате {msg_count:,} {decl_of_num(msg_count, self.msg_titles)} PogChamp")
  245. @commands.cooldown(rate=1, per=300, bucket=commands.Bucket.channel)
  246. @commands.command(name='всегонасрано')
  247. async def vsegonasrano(self, ctx: commands.Context):
  248. channel_user = await ctx.channel.user()
  249. msg_count = self.dbLogClient.get_all_users_message_count_for_mounth(channel_user.id)
  250. if not msg_count:
  251. await ctx.send(f'@{ctx.author.name}, не удалось подсчитать количество написаных сообщений NotLikeThis')
  252. return
  253. await ctx.send(f"В этом месяце в чате насрали {msg_count:,} сообщений SHTO")
  254. @commands.cooldown(rate=1, per=60, bucket=commands.Bucket.channel)
  255. @commands.command(name='маления')
  256. async def malenia(self, ctx: commands.Context):
  257. channel_user = await ctx.channel.user()
  258. msg_count = self.dbLogClient.get_malenia_in_channel(channel_user.id)
  259. if not msg_count:
  260. await ctx.send(f'@{ctx.author.name}, не удалось подсчитать упоминаний малений в этом чате NotLikeThis')
  261. return
  262. await ctx.send(f"В этом чате вспомнили Малению {msg_count:,} раз MaleniaTime")
  263. @commands.cooldown(rate=1, per=10, bucket=commands.Bucket.channel)
  264. @commands.command(name='год', aliases=['year', 'прогресс'])
  265. async def year(self, ctx: commands.Context):
  266. now = datetime.datetime.now()
  267. start_of_year = now.replace(day=1, month=1, year=now.year)
  268. days_passed = (now - start_of_year).days
  269. seconds_since_midnight = (now - now.replace(hour=0, minute=0, second=0, microsecond=0)).total_seconds()
  270. await ctx.send(f"@{ctx.author.name}, прогресс года: {(days_passed * 86400 + seconds_since_midnight) / ((365 + calendar.isleap(datetime.datetime.now().year)) * 86400) * 100:.10f}% catDespair")
  271. @commands.cooldown(rate=1, per=600, bucket=commands.Bucket.user)
  272. @commands.command(name='сосиска')
  273. async def sousage(self, ctx: commands.Context):
  274. length = random.randint(0, 30)
  275. emote = get_val_by_max_val({5: "PoroSad",
  276. 10: "Stare",
  277. 15: "Hmm",
  278. 20: "Hmmege",
  279. 25: "SHTO",
  280. 30: "EZ"}, length)
  281. await ctx.send(f"@{ctx.author.name} имеет сосиску {length} см. {emote}")
  282. #Рутины
  283. @routines.routine(time = datetime.datetime(year = 2024, month = 6, day = 1, hour = 18, minute = 00))
  284. async def ogey_of_day_routine(self):
  285. for ch in OGEY_OF_DAY_CHANNELS:
  286. channels = await self.fetch_users([ch])
  287. channel_id = channels[0].id
  288. channel = self.get_channel(ch)
  289. ogey_id = self.dbLogClient.get_random_user_by_last_n_hours(channel_id, 24)
  290. if self.dbLogClient.update_ogey(channel_id, ogey_id):
  291. await channel.send(f'Ogey дня обновился. Чтобы узнать кто им стал введите команду !ogeyofday 4Head')
  292. else:
  293. await channel.send(f'Не удалось определить нового Ogey. PoroSad')
  294. #Команды для белого списка
  295. @commands.command(name='горячесть', aliases=['температура', 'темп', 'temp'])
  296. async def temperature(self, ctx: commands.Context):
  297. if ctx.author.name in white_list:
  298. cpu_t = CPUTemperature()
  299. await ctx.send(f'Моя горячесть равна {cpu_t.temperature} градусам')
  300. #Обработка исключений
  301. async def event_command_error(self, ctx, error: Exception) -> None:
  302. if isinstance(error, commands.CommandOnCooldown) and not await self.is_stream_online(ctx.channel):
  303. await ctx.send(f'Команда "{error.command.name}" заряжается, еще {int(error.retry_after)} сек.')
  304. print(error)
  305. #Событие подключения к чату
  306. async def event_join(self, channel: Channel, user: User):
  307. print(f'{datetime.datetime.now()}: Пользователь {user.name} вошел в чат {channel.name}')
  308. if channel.name == user.name:
  309. channel_user = await channel.user() #id отсутвует в User поэтому приходится запрашивать
  310. await channel.send(f'Привет, мир! KonCha')
  311. print(f'{datetime.datetime.now()}: Стример в чате {channel_user.name}')
  312. async def event_ready(self):
  313. #Старт рутин
  314. self.ogey_of_day_routine.start()
  315. #Дополнительные функции
  316. async def is_stream_online(self, channel) -> bool:
  317. chan_user = await channel.user()
  318. streams = await self.fetch_streams([chan_user.id])
  319. if len(streams) == 0:
  320. return False
  321. return True