mirror of
https://github.com/polyjitter/searchbot-discord.git
synced 2024-08-14 22:46:55 +00:00
New Features I think
This commit is contained in:
parent
946e9a4869
commit
61c2f6aecd
7 changed files with 240 additions and 64 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
@ -1,6 +1,8 @@
|
|||
# Bot Related
|
||||
config.json
|
||||
extensions/__pycache__/
|
||||
utils/__pycache__/
|
||||
.mypy_cache/
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
|
@ -52,6 +54,4 @@ coverage.xml
|
|||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
extensions/__pycache__/core.cpython-38.pyc
|
||||
extensions/__pycache__/core.cpython-38.pyc
|
||||
extensions/__pycache__/core.cpython-38.pyc
|
||||
|
||||
|
|
|
@ -1,23 +1,39 @@
|
|||
{
|
||||
"VERSION": "x.x type",
|
||||
"DESCRIPTION": "a basic base for bots, designed by taciturasa.",
|
||||
"REPO": "",
|
||||
"SERVER": "",
|
||||
"MAINTENANCE": false,
|
||||
"VERSION": "1.9 testing",
|
||||
"DESCRIPTION": "a minimalist search utility bot for discord, designed by taciturasa.",
|
||||
"REPO": "https://github.com/taciturasa/searchbot-discord",
|
||||
"SERVER": "https://discord.gg/4BpReNV",
|
||||
"TOKEN": "",
|
||||
"DBL": "",
|
||||
"DBOTS": "",
|
||||
"BOD": "",
|
||||
"DBLCOM": "",
|
||||
"PREFIX": ["basicbot!"],
|
||||
"PREFIX": ["search!"],
|
||||
|
||||
"MAINTENANCE": false,
|
||||
"CASE_INSENSITIVE": true,
|
||||
"PREFIXLESS_DMS": true,
|
||||
"MENTION_ASSIST": true,
|
||||
"CUSTOM_HELP": true,
|
||||
"INFO_HOOK": "",
|
||||
"WARN_HOOK": "",
|
||||
"ERROR_HOOK": "",
|
||||
"DEBUG_HOOK": "",
|
||||
"PERMS": null,
|
||||
"BLOCKED": []
|
||||
}
|
||||
|
||||
"PERMS": 378944,
|
||||
"BLOCKED": [],
|
||||
|
||||
"BOTLISTS": {
|
||||
"DBL": "",
|
||||
"DBOTS": "",
|
||||
"BOD": "",
|
||||
"DBLCOM": ""
|
||||
},
|
||||
|
||||
"HOOKS": {
|
||||
"INFO_HOOK": "",
|
||||
"WARN_HOOK": "",
|
||||
"ERROR_HOOK": "",
|
||||
"DEBUG_HOOK": ""
|
||||
},
|
||||
|
||||
"RETHINK": {
|
||||
"DB": "",
|
||||
"USERNAME": "",
|
||||
"PASSWORD": "",
|
||||
"HOST": "",
|
||||
"PORT": null
|
||||
}
|
||||
}
|
|
@ -23,10 +23,10 @@ class BotList(commands.Cog, name='Bot List'):
|
|||
self.emoji = "\U0001F5F3"
|
||||
|
||||
# List Tokens
|
||||
self.dbl_token = bot.config['DBL']
|
||||
self.dbots_token = bot.config['DBOTS']
|
||||
self.bod_token = bot.config['BOD']
|
||||
self.dblcom_token = bot.config['DBLCOM']
|
||||
self.dbl_token = bot.config['BOTLISTS']['DBL']
|
||||
self.dbots_token = bot.config['BOTLISTS']['DBOTS']
|
||||
self.bod_token = bot.config['BOTLISTS']['BOD']
|
||||
self.dblcom_token = bot.config['BOTLISTS']['DBLCOM']
|
||||
|
||||
# top.gg client
|
||||
self.dbl_client = dbl.DBLClient(
|
||||
|
@ -139,7 +139,7 @@ class BotList(commands.Cog, name='Bot List'):
|
|||
@tasks.loop(minutes=15.0)
|
||||
async def update_stats(self):
|
||||
"""Automatically updates statistics every 15 minutes."""
|
||||
|
||||
|
||||
responses = await self._update_logic()
|
||||
print(responses) # TODO See other todo
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import cpuinfo
|
|||
import math
|
||||
import psutil
|
||||
from extensions.models.help import TaciHelpCommand
|
||||
from typing import List, Optional
|
||||
|
||||
|
||||
class Core(commands.Cog):
|
||||
|
@ -29,11 +30,37 @@ class Core(commands.Cog):
|
|||
|
||||
# Help Command
|
||||
self._original_help_command = bot.help_command
|
||||
if bot.config['CUSTOM_HELP']:
|
||||
if bot.custom_help:
|
||||
bot.help_command = TaciHelpCommand()
|
||||
bot.help_command.cog = self
|
||||
|
||||
def _humanbytes(self, B) -> str: # function lifted from StackOverflow
|
||||
def _create_tutorial(self, guild) -> str:
|
||||
"""Creates the tutorial message."""
|
||||
|
||||
prefixes: str = f"`@{self.bot.user.name}`"
|
||||
if self.bot.prefix:
|
||||
others: str = ', '.join(f'`{p}`' for p in self.bot.prefix)
|
||||
prefixes += f', {others}'
|
||||
|
||||
msg: str = (
|
||||
f"**Hi!** Thanks for adding me to `{guild.name}`.\n\n"
|
||||
f"I'm **{self.bot.user.name}** - _{self.bot.description}_\n\n"
|
||||
f"My prefix{'es are' if self.bot.prefix else ' is'}: "
|
||||
f"{prefixes}.\n\n"
|
||||
"You may find more information with `help`.\n\n"
|
||||
"_Please note that this bot may log errors, guild names, "
|
||||
"command calls/contents, and the names of command users "
|
||||
"for debug and maintenance purposes. "
|
||||
"These logs are shared with nobody "
|
||||
"other than those who help develop this bot. "
|
||||
"If you do not agree to this, please remove this bot._\n\n"
|
||||
"_You may recall this message at any time with `tutorial`._"
|
||||
)
|
||||
|
||||
return msg
|
||||
|
||||
# function lifted from StackOverflow
|
||||
def _humanbytes(self, B) -> str:
|
||||
"""Return the given bytes as a human friendly KB, MB, GB, or TB string."""
|
||||
|
||||
B = float(B)
|
||||
|
@ -53,6 +80,8 @@ class Core(commands.Cog):
|
|||
return '{0:.2f} GB'.format(B/GB)
|
||||
elif TB <= B:
|
||||
return '{0:.2f} TB'.format(B/TB)
|
||||
else:
|
||||
return 'ERROR'
|
||||
|
||||
@commands.command(aliases=['info', 'source', 'server'])
|
||||
async def about(self, ctx):
|
||||
|
@ -60,10 +89,10 @@ class Core(commands.Cog):
|
|||
|
||||
msg = f"__**{self.bot.user.name}**__ - _{self.bot.description}_\n\n"
|
||||
msg += f"This instance by **{self.bot.appinfo.owner}.**\n\n"
|
||||
if self.bot.config['REPO']:
|
||||
msg += f"**Source Code:** _<{self.bot.config['REPO']}>_\n"
|
||||
if self.bot.config['SERVER:']:
|
||||
msg += f"**Support Server:** _<{self.bot.config['SERVER']}>_\n\n"
|
||||
if self.bot.repo:
|
||||
msg += f"**Source Code:** _<{self.bot.repo}>_\n"
|
||||
if self.bot.support_server:
|
||||
msg += f"**Support Server:** _<{self.bot.support_server}>_\n\n"
|
||||
msg += "_Note: Please attempt to contact the hoster of any separate instances before this server._\n"
|
||||
msg += f"_See **{ctx.prefix}**`help` for help, `invite` to add the bot, and `stats` for statistics._"
|
||||
|
||||
|
@ -79,13 +108,23 @@ class Core(commands.Cog):
|
|||
f"*<https://discordapp.com/oauth2/authorize?client_id={self.bot.user.id}&scope=bot"
|
||||
)
|
||||
|
||||
if self.bot.config['PERMS'] is not None:
|
||||
msg += f"&permissions={self.bot.config['PERMS']}>*"
|
||||
if self.bot.perms:
|
||||
msg += f"&permissions={self.bot.perms}>*"
|
||||
else:
|
||||
msg += ">*"
|
||||
|
||||
await ctx.send(msg)
|
||||
|
||||
@commands.command()
|
||||
async def tutorial(self, ctx):
|
||||
"""Resends the tutorial message."""
|
||||
if ctx.guild:
|
||||
msg: str = self._create_tutorial(ctx.guild)
|
||||
else:
|
||||
msg: str = "**Cannot send tutorial in DMs!**"
|
||||
|
||||
await ctx.send(msg)
|
||||
|
||||
@commands.command()
|
||||
async def stats(self, ctx):
|
||||
"""Provides statistics on the bot itself."""
|
||||
|
@ -116,10 +155,10 @@ Number of extensions present: {len(ctx.bot.cogs)}
|
|||
ping = (after - before) * 1000
|
||||
await pong.edit(content="`PING discordapp.com {}ms`".format(int(ping)))
|
||||
|
||||
@commands.group(aliases=['extensions', 'ext'],
|
||||
@commands.group(aliases=['extensions', 'ext'],
|
||||
invoke_without_command=True)
|
||||
@commands.is_owner()
|
||||
async def extend(self, ctx, name:str = None):
|
||||
async def extend(self, ctx, name: str = None):
|
||||
"""Provides status of extensions and lets you hotswap extensions."""
|
||||
|
||||
# Provides status of extension
|
||||
|
@ -212,19 +251,81 @@ Number of extensions present: {len(ctx.bot.cogs)}
|
|||
@commands.is_owner()
|
||||
async def leave(self, ctx):
|
||||
"""Makes the bot leave the server this was called in."""
|
||||
|
||||
|
||||
if ctx.guild:
|
||||
await ctx.send(
|
||||
"\U0001F4A8 **Leaving server.** "
|
||||
"_If you want me back, add me or get an admin to._")
|
||||
"_If you want me back, add me or get an admin to._"
|
||||
)
|
||||
await ctx.guild.leave()
|
||||
else:
|
||||
await ctx.send(
|
||||
"**Can't leave!** _This channel is not inside a guild._")
|
||||
"**Can't leave!** _This channel is not inside a guild._"
|
||||
)
|
||||
|
||||
def cog_unload(self):
|
||||
self.bot.help_command = self._original_help_command
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_guild_join(self, guild):
|
||||
"""Sends owner notification and guild tutorial."""
|
||||
|
||||
# Prerequisites
|
||||
guild_msg: str = self._create_tutorial(guild)
|
||||
channel: Optional[discord.TextChannel] = None
|
||||
owner: discord.Member = guild.owner
|
||||
|
||||
# Tutorial Message
|
||||
# Get text channels
|
||||
text_channels = []
|
||||
for c in guild.channels:
|
||||
if type(c) is discord.TextChannel:
|
||||
text_channels.append(c)
|
||||
|
||||
# Sets channel to general if it exists
|
||||
for c in guild.channels:
|
||||
if c.name == 'general':
|
||||
channel = c
|
||||
|
||||
# XXX This looks like garbage
|
||||
# Else posts in first open channel
|
||||
if not channel:
|
||||
for c in guild.channels:
|
||||
if c.permissions_for(guild.me).send_messages:
|
||||
channel = c
|
||||
|
||||
# Send tutorial message
|
||||
if channel:
|
||||
await channel.send(guild_msg)
|
||||
else:
|
||||
guild_msg += (
|
||||
"\n\n_I am sending this message to you as there were no "
|
||||
"channels I could send messages to in your server. "
|
||||
"Please give me send message permissions in the channels "
|
||||
"You wish to use me in!_"
|
||||
)
|
||||
|
||||
await guild.owner.send(guild_msg)
|
||||
return # Ends here if there are no good channels to send to
|
||||
|
||||
# Owner Disclosure
|
||||
# Message Building
|
||||
owner_msg = (
|
||||
"**Hi there!**\n\n"
|
||||
f"I am **{self.bot.user.name}** - _{self.bot.description}_\n\n"
|
||||
"I am messaging you to inform you I was added to your server, "
|
||||
f"`{guild.name}`, by someone "
|
||||
"with **Manage Server** permissions.\n\n"
|
||||
f"I have sent a tutorial message to `{channel.name}` "
|
||||
"describing how I may be used.\n\n"
|
||||
"If you do not wish to have me there, "
|
||||
"simply kick me from the server.\n\n"
|
||||
"_Thanks for your time!_"
|
||||
)
|
||||
|
||||
# Send owner disclosure
|
||||
await guild.owner.send(owner_msg)
|
||||
|
||||
|
||||
def setup(bot):
|
||||
bot.add_cog(Core(bot))
|
||||
|
|
|
@ -23,9 +23,13 @@ class Search(commands.Cog):
|
|||
self.info = bot.logging.info
|
||||
self.warn = bot.logging.warn
|
||||
self.request = bot.request
|
||||
self.instances = bot.instances
|
||||
self.emoji = "\U0001F50D"
|
||||
|
||||
# Get Instances
|
||||
with open('searxes.txt') as f:
|
||||
self.instances = f.read().split('\n')
|
||||
|
||||
|
||||
async def _search_logic(self, query: str, is_nsfw: bool = False,
|
||||
category: str = None) -> str:
|
||||
"""Provides search logic for all search commands."""
|
||||
|
|
|
@ -22,22 +22,22 @@ class Logging():
|
|||
|
||||
# Sets info hook first
|
||||
self.info_hook = self.online.get_webhook(
|
||||
bot.config['INFO_HOOK'] if bot.config['INFO_HOOK']
|
||||
bot.config['HOOKS']['INFO_HOOK'] if bot.config['HOOKS']['INFO_HOOK']
|
||||
else None
|
||||
)
|
||||
|
||||
# Sets other hooks or defaults them
|
||||
if self.info_hook:
|
||||
self.warn_hook = self.online.get_webhook(
|
||||
bot.config['WARN_HOOK'] if bot.config['WARN_HOOK']
|
||||
bot.config['HOOKS']['WARN_HOOK'] if bot.config['HOOKS']['WARN_HOOK']
|
||||
else self.info_hook
|
||||
)
|
||||
self.error_hook = self.online.get_webhook(
|
||||
bot.config['ERROR_HOOK'] if bot.config['ERROR_HOOK']
|
||||
bot.config['HOOKS']['ERROR_HOOK'] if bot.config['HOOKS']['ERROR_HOOK']
|
||||
else self.info_hook
|
||||
)
|
||||
self.debug_hook = self.online.get_webhook(
|
||||
bot.config['DEBUG_HOOK'] if bot.config['DEBUG_HOOK']
|
||||
bot.config['HOOKS']['DEBUG_HOOK'] if bot.config['HOOKS']['DEBUG_HOOK']
|
||||
else self.info_hook
|
||||
)
|
||||
|
||||
|
|
97
main.py
97
main.py
|
@ -12,10 +12,12 @@ from discord.ext import commands
|
|||
import traceback
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import asyncio
|
||||
import aiohttp
|
||||
import logging
|
||||
import random
|
||||
import rethinkdb
|
||||
from typing import List, Optional
|
||||
|
||||
|
||||
class Bot(commands.Bot):
|
||||
"""Custom Bot Class that subclasses the commands.ext one"""
|
||||
|
@ -27,24 +29,38 @@ class Bot(commands.Bot):
|
|||
super().__init__(self._get_prefix_new, **options)
|
||||
|
||||
# Setup
|
||||
self.extensions_list = []
|
||||
self.extensions_list: List[str] = []
|
||||
|
||||
with open('config.json') as f:
|
||||
self.config = json.load(f)
|
||||
self.prefix = self.config['PREFIX']
|
||||
self.version = self.config['VERSION']
|
||||
self.maintenance = self.config['MAINTENANCE']
|
||||
self.description = self.config['DESCRIPTION']
|
||||
self.case_insensitive = self.config['CASE_INSENSITIVE']
|
||||
|
||||
# Get Instances
|
||||
with open('searxes.txt') as f:
|
||||
self.instances = f.read().split('\n')
|
||||
# Info
|
||||
self.prefix: List[str] = self.config['PREFIX']
|
||||
self.version: str = self.config['VERSION']
|
||||
self.description: str = self.config['DESCRIPTION']
|
||||
self.repo: str = self.config['REPO']
|
||||
self.support_server: str = self.config['SERVER']
|
||||
self.perms: int = self.config['PERMS']
|
||||
|
||||
# Toggles
|
||||
self.maintenance: bool = self.config['MAINTENANCE']
|
||||
self.case_insensitive: bool = self.config['CASE_INSENSITIVE']
|
||||
self.custom_help: bool = self.config['CUSTOM_HELP']
|
||||
self.mention_assist: bool = self.config['MENTION_ASSIST']
|
||||
self.prefixless_dms: bool = self.config['PREFIXLESS_DMS']
|
||||
|
||||
# RethinkDB
|
||||
if self.config['RETHINK']['DB']:
|
||||
self.re = rethinkdb.RethinkDB()
|
||||
self.re.set_loop_type('asyncio')
|
||||
self.rdb: str = self.config['RETHINK']['DB']
|
||||
self.conn = None
|
||||
self.rtables: List[str] = []
|
||||
|
||||
def _init_extensions(self):
|
||||
"""Initializes extensions."""
|
||||
|
||||
# Utils
|
||||
|
||||
# Avoids race conditions with online
|
||||
utils_dir = os.listdir('extensions/utils')
|
||||
if 'online.py' in utils_dir:
|
||||
|
@ -80,16 +96,50 @@ class Bot(commands.Bot):
|
|||
f'extensions.{ext[:-3]}')
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
async def _init_rethinkdb(self):
|
||||
"""Initializes RethinkDB."""
|
||||
|
||||
# Prerequisites
|
||||
dbc = self.config['RETHINK']
|
||||
|
||||
# Error handling the initialization
|
||||
try:
|
||||
# Create connection
|
||||
self.conn = await self.re.connect(
|
||||
host=dbc['HOST'],
|
||||
port=dbc['PORT'],
|
||||
db=dbc['DB'],
|
||||
user=dbc['USERNAME'],
|
||||
password=dbc['PASSWORD']
|
||||
)
|
||||
|
||||
# Create or get database
|
||||
dbs = await self.re.db_list().run(self.conn)
|
||||
if self.rdb not in dbs:
|
||||
print('Database not present. Creating...')
|
||||
await self.re.db_create(self.rdb).run(self.conn)
|
||||
|
||||
# Append any existing tables to rtables
|
||||
tables = await self.re.db(self.rdb).table_list().run(self.conn)
|
||||
self.rtables.extend(tables)
|
||||
|
||||
# Exit if fails bc bot can't run without db
|
||||
except Exception as e:
|
||||
print('RethinkDB init error!\n{}: {}'.format(type(e).__name__, e))
|
||||
sys.exit(1)
|
||||
|
||||
print('RethinkDB initialisation successful.')
|
||||
|
||||
async def _get_prefix_new(self, bot, msg):
|
||||
"""More flexible check for prefix."""
|
||||
|
||||
# Adds empty prefix if in DMs
|
||||
if isinstance(msg.channel, discord.DMChannel) and self.config['PREFIXLESS_DMS']:
|
||||
if isinstance(msg.channel, discord.DMChannel) and self.prefixless_dms:
|
||||
plus_empty = self.prefix.copy()
|
||||
plus_empty.append('')
|
||||
return commands.when_mentioned_or(*plus_empty)(bot, msg)
|
||||
|
||||
# Keeps regular if not
|
||||
else:
|
||||
return commands.when_mentioned_or(*self.prefix)(bot, msg)
|
||||
|
@ -125,9 +175,14 @@ class Bot(commands.Bot):
|
|||
status=discord.Status.online
|
||||
)
|
||||
|
||||
# NOTE Rethink Entry Point
|
||||
# Initializes all rethink stuff
|
||||
if hasattr(self, 'rdb') and not self.rtables:
|
||||
await self._init_rethinkdb()
|
||||
|
||||
# NOTE Extension Entry Point
|
||||
# Loads core, which loads all other extensions
|
||||
if self.extensions_list == []:
|
||||
if not self.extensions_list:
|
||||
self._init_extensions()
|
||||
|
||||
print('Initialized.\n')
|
||||
|
@ -167,7 +222,7 @@ class Bot(commands.Bot):
|
|||
return
|
||||
|
||||
# Empty ping for assistance
|
||||
elif message.content in mentions and self.config.get('MENTION_ASSIST'):
|
||||
elif message.content in mentions and self.mention_assist:
|
||||
assist_msg = (
|
||||
"**Hi there! How can I help?**\n\n"
|
||||
# Two New Lines Here
|
||||
|
@ -197,12 +252,12 @@ async def on_command_error(ctx, error):
|
|||
elif isinstance(error, commands.CommandInvokeError):
|
||||
|
||||
# Prerequisites
|
||||
embed_fallback = f"**An error occured: {type(error).__name__}. Please contact {bot.appinfo.owner}.**"
|
||||
embed_fallback = f"**An error occured: {type(error).__name__}. Please contact {bot.appinfo.owner}.**"
|
||||
error_embed = await bot.logging.error(
|
||||
error, ctx,
|
||||
error, ctx,
|
||||
ctx.command.cog.qualified_name if ctx.command.cog.qualified_name
|
||||
else "DMs"
|
||||
)
|
||||
)
|
||||
|
||||
# Sending
|
||||
await ctx.send(embed_fallback, embed=error_embed)
|
||||
|
@ -210,7 +265,7 @@ async def on_command_error(ctx, error):
|
|||
# If anything else goes wrong, just go ahead and send it in chat.
|
||||
else:
|
||||
await bot.logging.error(
|
||||
error, ctx,
|
||||
error, ctx,
|
||||
ctx.command.cog.qualified_name if ctx.command.cog.qualified_name
|
||||
else "DMs"
|
||||
)
|
||||
|
@ -218,4 +273,4 @@ async def on_command_error(ctx, error):
|
|||
# NOTE Bot Entry Point
|
||||
# Starts the bot
|
||||
print("Connecting...\n")
|
||||
bot.run(bot.config['TOKEN'])
|
||||
bot.run(bot.config['TOKEN'])
|
Loading…
Reference in a new issue