├── .gitignore ├── LICENSE ├── MARTA ├── marta.py ├── martapi │ ├── __init__.py │ ├── api.py │ ├── exceptions.py │ └── vehicles.py └── settings.py ├── README.md ├── bot.py ├── discord_cogs ├── __init__.py ├── roles.py └── vc_gaming_manager.py ├── general ├── coronavirus.py └── speedtest.py ├── helper ├── cog_handler.py ├── cog_list.py ├── db_commands.py ├── discord_helper.py ├── dropbox_handler.py ├── encryption.py ├── helper_functions.py └── pastebin.py ├── media_server ├── booksonic │ ├── __init__.py │ ├── booksonic.py │ └── settings.py ├── deluge │ ├── __init__.py │ ├── deluge.py │ └── settings.py ├── discordConnector.db ├── emby │ ├── __init__.py │ ├── emby_api.py │ ├── emby_manager.py │ └── settings.py ├── jellyfin │ ├── jellyfin.py │ ├── jellyfin_api.py │ ├── jellyfin_manager.py │ ├── jellyfin_recs.py │ ├── jellyfin_stats.py │ └── settings.py ├── migrate.sql ├── olaris │ ├── olaris_api.py │ ├── olaris_manager.py │ └── settings.py ├── plex │ ├── plex.py │ ├── plex_api.py │ ├── plex_manager.py │ ├── plex_manager_nodb.py │ ├── plex_recs.py │ └── settings.py └── rclone │ ├── rclone.py │ └── settings.py ├── news └── news.py ├── requirements.txt ├── settings.py ├── smart_home ├── google_home │ └── google_home.py ├── sengled_lights │ ├── requirements.txt │ └── sengled.py └── wink │ └── wink.py └── sports ├── espn ├── __init__.py ├── espn.py ├── espn_dict.txt ├── info.json └── logo.png └── yahoofantasy ├── __init__.py ├── settings.py └── yahoofantasy.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | 59 | # Private configs 60 | config.json 61 | config.txt 62 | *oauth2.json 63 | -------------------------------------------------------------------------------- /MARTA/martapi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nwithan8/Arca/a98ae7f2defb2c8324835273106cb7c9034185e7/MARTA/martapi/__init__.py -------------------------------------------------------------------------------- /MARTA/martapi/api.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import requests_cache 3 | from json import loads 4 | from os import getenv 5 | from functools import wraps 6 | 7 | from .exceptions import APIKeyError 8 | from .vehicles import Bus, Train 9 | 10 | import MARTA.settings as settings 11 | 12 | _API_KEY = settings.MARTA_API_KEY 13 | _CACHE_EXPIRE = 30 14 | _BASE_URL = 'http://developer.itsmarta.com' 15 | _TRAIN_PATH = '/RealtimeTrain/RestServiceNextTrain/GetRealtimeArrivals' 16 | _BUS_PATH = '/BRDRestService/RestBusRealTimeService/GetAllBus' 17 | _BUS_ROUTE_PATH = '/BRDRestService/RestBusRealTimeService/GetBusByRoute/' 18 | 19 | # requests_cache.install_cache('marta_api_cache', backend='sqlite', expire_after=_CACHE_EXPIRE) 20 | 21 | 22 | def require_api_key(func): 23 | """ 24 | Decorator to ensure an API key is present 25 | """ 26 | @wraps(func) 27 | def with_key(*args, **kwargs): 28 | api_key = kwargs.get('api_key') 29 | if not api_key and not _API_KEY: 30 | raise APIKeyError() 31 | kwargs['api_key'] = api_key if api_key else _API_KEY 32 | return func(*args, **kwargs) 33 | return with_key 34 | 35 | 36 | @require_api_key 37 | def get_trains(line=None, station=None, destination=None, api_key=None): 38 | """ 39 | Query API for train information 40 | :param line (str): train line identifier filter (red, gold, green, or blue) 41 | :param station (str): train station filter 42 | :param destination (str): destination filter 43 | :param api_key (str): API key to override environment variable 44 | :return: list of Train objects 45 | """ 46 | endpoint = '{}{}?apikey={}'.format(_BASE_URL, _TRAIN_PATH, api_key) 47 | response = requests.get(endpoint) 48 | 49 | if response.status_code == 401 or response.status_code == 403: 50 | raise APIKeyError('Your API key seems to be invalid. Try visiting {}.'.format(endpoint)) 51 | 52 | data = loads(response.text) 53 | trains = [Train(t) for t in data] 54 | 55 | # We only want results that match our criteria. This is done in a single list comprehension 56 | # to avoid iterating over the response multiple times. 57 | # Read as, for example, "if line parameter is specified, only include results for that line" 58 | # TODO: Make this filtering more intuitive. 59 | trains = [t for t in trains if 60 | (t.line.lower() == line.lower() if line is not None else True) and 61 | (t.station.lower() == station.lower() if station is not None else True) and 62 | (t.destination.lower() == destination.lower() if destination is not None else True)] 63 | 64 | return trains 65 | 66 | 67 | @require_api_key 68 | def get_buses(route=None, api_key=None): 69 | """ 70 | Query API for bus information 71 | :param route (int): route number 72 | :param api_key (str): API key to override environment variable 73 | :return: list of Bus objects 74 | """ 75 | if route is not None: 76 | url = '{}{}/{}?apikey={}'.format(_BASE_URL, _BUS_ROUTE_PATH, str(route), api_key) 77 | else: 78 | url = '{}{}?apikey={}'.format(_BASE_URL, _BUS_PATH, _API_KEY) 79 | 80 | response = requests.get(url) 81 | 82 | if response.status_code == 401 or response.status_code == 403: 83 | raise APIKeyError('Your API key seems to be invalid. Try visiting {}.'.format(url)) 84 | 85 | data = loads(response.text) 86 | return [Bus(b) for b in data] 87 | -------------------------------------------------------------------------------- /MARTA/martapi/exceptions.py: -------------------------------------------------------------------------------- 1 | class APIKeyError(Exception): 2 | """Exception thrown for a missing API key""" 3 | def __init__(self, message=None): 4 | 5 | if not message: 6 | message = 'API Key is missing. Please set MARTA_API_KEY or use api_key kwarg.' 7 | super(Exception, self).__init__(message) 8 | -------------------------------------------------------------------------------- /MARTA/martapi/vehicles.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | 4 | class Vehicle(): 5 | """Generic Vehicle object that exists to print vehicles as dicts""" 6 | def __str__(self): 7 | return str(self.__dict__) 8 | 9 | 10 | class Bus(Vehicle): 11 | def __init__(self, record): 12 | self.adherence = record.get('ADHERENCE') 13 | self.block_id = record.get('BLOCKID') 14 | self.block_abbr = record.get('BLOCK_ABBR') 15 | self.direction = record.get('DIRECTION') 16 | self.latitude = record.get('LATITUDE') 17 | self.longitude = record.get('LONGITUDE') 18 | self.last_updated = datetime.strptime(record.get('MSGTIME'), '%m/%d/%Y %H:%M:%S %p') 19 | self.route = int(record.get('ROUTE')) 20 | self.stop_id = record.get('STOPID') 21 | self.timepoint = record.get('TIMEPOINT') 22 | self.trip_id = record.get('TRIPID') 23 | self.vehicle = record.get('VEHICLE') 24 | 25 | 26 | class Train(Vehicle): 27 | def __init__(self, record): 28 | self.destination = record.get('DESTINATION') 29 | self.direction = record.get('DIRECTION') 30 | self.last_updated = datetime.strptime(record.get('EVENT_TIME'), '%m/%d/%Y %H:%M:%S %p') 31 | self.line = record.get('LINE') 32 | self.next_arrival = datetime.strptime(record.get('NEXT_ARR'), '%H:%M:%S %p').time() 33 | self.station = record.get('STATION') 34 | self.train_id = record.get('TRAIN_ID') 35 | self.waiting_seconds = record.get('WAITING_SECONDS') 36 | self.waiting_time = record.get('WAITING_TIME') 37 | -------------------------------------------------------------------------------- /MARTA/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | MARTA_API_KEY = os.environ.get('MARTA_API_KEY') -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nwithan8 Cogs 2 | 3 | DEPRECATED: `Arca` is no longer being maintained. Components are being extracted and converted into [RedBot](https://github.com/Cog-Creators/Red-DiscordBot)-compatible extensions (cogs): https://github.com/nwithan8/nwithan8-cogs 4 | 5 | Cog Function 6 | 7 | | Name | Status | Description (Click for full details) 8 | | --- | --- | --- | 9 | | espn | 2.1.0 |
Get live data from ESPN.com

Commands:

Supported leagues: NFL, NBA, MLB, NHL, CFB, CBBM, CBBW

| 10 | | plex | 1.6.0 |
Interact with a Plex Media Server (via Tautulli)

Commands:

| 11 | | plex_manager | 2.1.0 |
Manage a Plex Media Server

Commands:

  • add - Invite Plex user to Plex server
  • remove - Remove Plex user from Plex server
  • details - See a Plex user's library, ratings and sync restrictions
  • edit - Change a Plex user's library, ratings and sync restrictions
  • import - Import existing Plex users to database
  • trial - Start a trial of the Plex server
  • winner - List winner Plex usernames
  • purge - Remove inactive winners
  • cleandb - Delete outdated database entries if users were removed manually from Plex
  • li>blacklist - Blacklist a Discord user or Plex username
  • count - Get the number of Plex Friends with access to the Plex server
  • access - Check if a user has access to the Plex server
  • find - Find a user based on Plex or Discord username
  • info - Get database entry for a user
  • status - Check if Plex is up and running
| 12 | | jellyfin | 0.6.0 |
Interact with a Jellyfin Media Server

Commands:

  • rec - Get a recommendation of what to watch
  • now - View and manage live Jellyfin streams
| 13 | | jellyfin_manager | 2.2.0 |
Manage a Jellyfin Media Server

Commands:

  • add - Create Jellyfin user
  • remove - Disable Jellyfin user
  • details - See a Jellyfin user's library, live TV and download restrictions
  • edit - Change a Jellyfin user's library, live TV and download restrictions
  • import - Import existing Jellyfin users to database
  • trial - Start a trial of the Jellyfin server
  • winner - List winner Jellyfin usernames
  • purge - Remove inactive winners
  • cleandb - Delete outdated database entries if users were removed manually from Jellyfin
  • blacklist - Blacklist a Discord user or Jellyfin username
  • count - Get the number of Jellyfin users with access to the Jellyfin server
  • access - Check if a user has access to the Jellyfin server
  • find - Find a user based on Jellyfin or Discord username
  • info - Get database entry for a user
  • status - Check if Jellyfin is up and running
| 14 | | emby_manager | 2.1.1 |
Manage an Emby Media Server

Commands:

  • add - Create Emby user (optionally link to Emby Connect username)
  • remove - Disable Emby user
  • import - Import existing Emby users to database
  • trial - Start a trial of the Emby server
  • winner - List winner Emby usernames
  • purge - Remove inactive winners
  • cleandb - Delete outdated database entries if users were removed manually from Emby
  • blacklist - Blacklist a Discord user or Emby username
  • count - Get the number of Emby users with access to the Emby server
  • access - Check if a user has access to the Emby server
  • find - Find a user based on Emby or Discord username
  • info - Get database entry for a user
  • status - Check if Emby is up and running
| 15 | | cog_handler | 0.2.0 |
Manage cogs for Discord bot

Commands:

  • enable - Enable cogs without restarting the bot (incompatible with RedBot cogs)
  • disable - Disable cogs without restarting the bot
  • restart - Reload a cog without restarting the bot
  • download - Download cogs from Dropbox
  • upload - Upload cogs to Dropbox
  • repo - Clone repos from .git URLs
| 16 | | news | 1.0.0 |
Get news headlines

Commands:

  • brief - Get 5 top headlines
  • top - Top headlines from a specific media outlet
  • sports - Sports news headlines
  • u.s. - U.S. news headlines
  • world - World news headlines
| 17 | | marta | 1.1.0 |
Get MARTA train info

MARTA is the Metro Atlanta Rapid Transit Authority, the light-rail system in Atlanta, Georgia

Commands:

  • trains - Get live train arrival times
  • time - How long to go from one station to another
  • stations - List available stations
| 18 | | roles | 1.0.0 |
Mass-add and remove Discord roles

Commands:

  • add - Add roles to users
  • remove - Remove roles from users
  • list - List available roles to add/remove
| 19 | | sengled | 1.0.0 |
Control Sengled smart lights

Commands:

  • lights - Toggle light on/off states and brightness
| 20 | | wink | 1.0.0 |
Control Wink Hub-connected smart lights

Commands:

  • wink - List and toggle device and group on/off states
  • color - Alter light color
| 21 | 22 | # Installation 23 | 1. ```git clone https://github.com/nwithan8/nwithan8-cogs.git``` 24 | 2. Edit settings in respective 'settings.py' files. 25 | 3. Install required packages with ```pip3 install -r requirements.txt``` 26 | 5. Run with ```./bot.py``` 27 | 28 | ```bot.py``` has some cogs activated by default. Cogs can be deactivated by being commented out. 29 | 30 | Individual cogs can also be installed to pre-existing bots as well. 31 | 32 | Demo video: https://www.youtube.com/watch?v=6etsv5b0IRs 33 | 34 | # IMPORTANT UPGRADE NOTE 35 | Databases for each media server type (Plex, Emby, Jellyfin) has been combined into one database file. 36 | You can migrate your data by running ```sqlite3 discordConnector.db < migrate.sql``` from the ```media_server``` folder. 37 | 38 | # Usage 39 | Default bot prefix is ```*``` 40 | Prefix can be changed in ```bot.py``` 41 | 42 | Type ```*help``` to get overview of installed cogs. 43 | 44 | # Contact 45 | Join the #cogs channel on my Discord server: 46 | 47 |
48 |

49 | 50 |

51 |
52 | 53 | I'm also often floating around on other developer Discord servers. 54 | 55 | Discord: nwithan8#8438 56 | 57 | # Credits 58 | [callmekory](https://github.com/callmekory) 59 | 60 | The people in the [r/Discord_Bots server](https://discord.gg/49wYxqk) 61 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # import helper.log as log 4 | import discord 5 | from discord.ext import commands 6 | import helper.cog_handler as cog_handler 7 | import settings as settings 8 | 9 | bot = commands.Bot(command_prefix=settings.PREFIX) 10 | 11 | formatter = commands.HelpCommand(show_check_failure=False) 12 | 13 | exts = settings.extensions 14 | 15 | if settings.USE_REMOTE_CONFIG: 16 | settings.USE_DROPBOX = True 17 | # You can only use cog_handler if you fill out the DROPBOX_API_KEY environmental variable 18 | cog_handler.USE_REMOTE_CONFIG = True 19 | exts = cog_handler.load_remote_config("cogs.txt") 20 | for ext in exts: 21 | path = cog_handler.find_cog_path_by_name(ext) 22 | if path: 23 | # log.info(f"Loading {path}...") 24 | print(f"Loading {path}...") 25 | bot.load_extension(path) 26 | if settings.USE_DROPBOX: 27 | cog_handler.USE_DROPBOX = True # USE_DROPBOX has to be enabled for remote config to work 28 | bot.load_extension("helper.cog_handler") # Always enabled, never disabled 29 | 30 | 31 | @bot.event 32 | async def on_ready(): 33 | print(f'\n\nLogged in as : {bot.user.name} - {bot.user.id}\nVersion: {discord.__version__}\n') 34 | await bot.change_presence(status=discord.Status.idle, activity=discord.Game(name=f'the waiting game | {settings.PREFIX}')) 35 | print(f'Successfully logged in and booted...!\n') 36 | 37 | 38 | print("Arca Copyright (C) 2020 Nathan Harris\nThis program comes with ABSOLUTELY NO WARRANTY\nThis is free " 39 | "software, and you are welcome to redistribute it\nunder certain conditions.") 40 | 41 | bot.run(settings.BOT_TOKEN) 42 | -------------------------------------------------------------------------------- /discord_cogs/__init__.py: -------------------------------------------------------------------------------- 1 | import discord_cogs.vc_gaming_manager as VC 2 | 3 | def setup(bot): 4 | VC.setup(bot) 5 | -------------------------------------------------------------------------------- /discord_cogs/roles.py: -------------------------------------------------------------------------------- 1 | """ 2 | Manage Discord roles 3 | Copyright (C) 2019 Nathan Harris 4 | """ 5 | 6 | import discord 7 | from discord.ext import commands, tasks 8 | from discord.utils import get 9 | import os 10 | import asyncio 11 | import helper.discord_helper as discord_helper 12 | 13 | ADMIN_ROLE_NAME = "Admin" 14 | 15 | REGULAR_ROLES_AVAILABLE = { 16 | # "Role Name": ["role", "nicknames","or keywords","to look for","in the message"], 17 | "Movie Night": ['mw', 'mn', 'movie'] 18 | } 19 | ADMIN_LOCKED_ROLES_AVAILABLE = { 20 | 21 | } 22 | 23 | 24 | async def add_roles(user, request, guild, admin=False): 25 | roles_added = "" 26 | for role_name, nicknames in REGULAR_ROLES_AVAILABLE.items(): 27 | for nick in nicknames: 28 | if nick in request: 29 | try: 30 | role = discord.utils.get(guild.roles, name=role_name) 31 | await user.add_roles(role, reason="Self-added") 32 | roles_added += role_name + ", " 33 | except Exception as e: 34 | print(e) 35 | pass 36 | if admin: 37 | for role_name, nicknames in ADMIN_LOCKED_ROLES_AVAILABLE.items(): 38 | if nick in request: 39 | try: 40 | role = discord.utils.get(guild.roles, name=role_name) 41 | await user.add_roles(role, reason="Self-added") 42 | roles_added += role_name + ", " 43 | except Exception as e: 44 | print(e) 45 | pass 46 | return roles_added 47 | 48 | 49 | async def remove_roles(user, request, guild, admin=False): 50 | roles_removed = "" 51 | for role_name, nicknames in REGULAR_ROLES_AVAILABLE.items(): 52 | for nick in nicknames: 53 | if nick in request: 54 | role = discord.utils.get(guild.roles, name=role_name) 55 | await user.remove_roles(role, reason="Self-added") 56 | roles_removed += role_name + ", " 57 | if admin: 58 | for role_name, nicknames in ADMIN_LOCKED_ROLES_AVAILABLE.items(): 59 | if nick in request: 60 | role = discord.utils.get(guild.roles, name=role_name) 61 | await user.remove_roles(role, reason="Self-added") 62 | roles_removed += role_name + ", " 63 | return roles_removed 64 | 65 | 66 | class Roles(commands.Cog): 67 | 68 | @commands.group(aliases=["role"], pass_context=True) 69 | async def roles(self, ctx: commands.Context): 70 | """ 71 | Manage roles 72 | """ 73 | if ctx.invoked_subcommand is None: 74 | await ctx.send("What subcommand?") 75 | 76 | @roles.command(name="list", pass_context=True) 77 | async def roles_list(self, ctx: commands.Context): 78 | """ 79 | List available roles 80 | """ 81 | roles = "" 82 | for role_name, nicknames in REGULAR_ROLES_AVAILABLE.items(): 83 | roles += role_name + ", " 84 | if discord_helper.user_has_role(ctx=ctx, user=ctx.author, role_name=ADMIN_ROLE_NAME): 85 | for role_name, nicknames in ADMIN_LOCKED_ROLES_AVAILABLE.items(): 86 | roles += role_name + ", " 87 | roles = roles[:-2] 88 | await ctx.send("Available roles:\n" + roles) 89 | 90 | @commands.has_role(ADMIN_ROLE_NAME) 91 | @roles.command(name="add", pass_context=True) 92 | async def roles_add(self, ctx: commands.Context, role: str, *, nicknames: str): 93 | """ 94 | Add new role to the list of available roles 95 | Use quotes if role is multiple words 96 | Recommended to use single-word nicknames 97 | Indicate "admin_only" to make roles only 98 | """ 99 | nicknames = nicknames.split() 100 | if 'admin_only' in nicknames: 101 | ADMIN_LOCKED_ROLES_AVAILABLE[role] = [] 102 | for name in nicknames: 103 | if name == 'admin_only': 104 | pass 105 | else: 106 | ADMIN_LOCKED_ROLES_AVAILABLE[role].append(name) 107 | await ctx.send("Role added to available admin-locked roles") 108 | else: 109 | REGULAR_ROLES_AVAILABLE[role] = [] 110 | for name in nicknames: 111 | REGULAR_ROLES_AVAILABLE[role].append(name) 112 | await ctx.send("Role added to available roles") 113 | 114 | @roles.command(name="assign", pass_context=True) 115 | async def roles_assign(self, ctx: commands.Context, *, roles: str): 116 | """ 117 | Assign roles to yourself or others 118 | Regular users can add multiple roles to themselves 119 | Admin users can add one role to multiple users 120 | """ 121 | added = "" 122 | if discord_helper.user_has_role(ctx=ctx, user=ctx.author, role_name=ADMIN_ROLE_NAME): 123 | if ctx.message.mentions: # add multiple roles to multiple users 124 | for u in ctx.message.mentions: 125 | added = await add_roles(u, roles, ctx.message.guild, True) 126 | if added: 127 | await ctx.send("Those users have received the following roles:\n" + added[:-2]) 128 | else: 129 | await ctx.send("No roles were added to those users.") 130 | else: # add multiple roles (admin) to the message author (admin) 131 | added = await add_roles(ctx.message.author, roles, ctx.message.guild, True) 132 | if added: 133 | await ctx.send("These roles were assigned to you:\n" + added[:-2]) 134 | else: 135 | await ctx.send("No new roles were assigned to you.") 136 | else: # not an admin, can only add roles (non-admin) to themselves 137 | added = await add_roles(ctx.message.author, roles, ctx.message.guild, False) 138 | if added: 139 | await ctx.send("These roles were assigned to you:\n" + added[:-2]) 140 | else: 141 | await ctx.send("No new roles were assigned to you.") 142 | 143 | @roles_assign.error 144 | async def roles_assign_error(self, ctx, error): 145 | print(error) 146 | await ctx.send("Something went wrong.") 147 | 148 | @roles.command(name="remove", pass_context=True) 149 | async def roles_remove(self, ctx: commands.Context, *, roles: str): 150 | """ 151 | Remove roles from yourself or others 152 | Regular users can remove multiple roles from themselves 153 | Admin users can remove one role from multiple users 154 | """ 155 | removed = "" 156 | if discord_helper.user_has_role(ctx=ctx, user=ctx.author, role_name=ADMIN_ROLE_NAME): 157 | if ctx.message.mentions: # remove multiple roles from multiple users 158 | for u in ctx.message.mentions: 159 | removed = await remove_roles(u, roles, ctx.message.guild, True) 160 | if removed: 161 | await ctx.send("Those users have lost the following roles:\n" + removed[:-2]) 162 | else: 163 | await ctx.send("No roles were removed from those users.") 164 | else: # remove multiple roles (admin) from the message author 165 | removed = await remove_roles(ctx.message.author, roles, ctx.message.guild, True) 166 | if removed: 167 | await ctx.send("These roles were unassigned to you:\n" + removed[:-2]) 168 | else: 169 | await ctx.send("No new roles were unassigned to you.") 170 | else: # not an admin, can only remove roles (non-admin) from themselves 171 | removed = await remove_roles(ctx.message.author, roles, ctx.message.guild, False) 172 | if removed: 173 | await ctx.send("These roles were unassigned to you:\n" + removed[:-2]) 174 | else: 175 | await ctx.send("No new roles were unassigned to you.") 176 | 177 | @roles_remove.error 178 | async def roles_remove_error(self, ctx, error): 179 | print(error) 180 | await ctx.send("Something went wrong.") 181 | 182 | def __init__(self, bot): 183 | self.bot = bot 184 | print("Roles ready to go!") 185 | 186 | 187 | def setup(bot): 188 | bot.add_cog(Roles(bot)) 189 | -------------------------------------------------------------------------------- /discord_cogs/vc_gaming_manager.py: -------------------------------------------------------------------------------- 1 | # Thanks to https://github.com/windfreaker for this script 2 | 3 | from discord import ActivityType 4 | from discord.ext import commands 5 | 6 | 7 | def name_generator(channel): 8 | game_names = [] 9 | for member in channel.members: 10 | for activity in member.activities: 11 | if activity.type is ActivityType.playing: 12 | game_names.append(activity.name) 13 | if len(game_names) == 0: 14 | return 'No Game Detected' 15 | elif len(game_names) == 1: 16 | return game_names[0] 17 | else: 18 | counter = 0 19 | prev_name = game_names[0] 20 | for name in game_names: 21 | if channel.name.startswith(name): 22 | prev_name = name 23 | else: 24 | counter += 1 25 | if counter != 0: 26 | return f'{prev_name} + {str(counter)}' 27 | else: 28 | return prev_name 29 | 30 | 31 | async def channel_joined(member, channel): 32 | if activator_checklist(channel): 33 | new_name = name_generator(channel) 34 | await channel.clone(reason=str(member) + ' joined activator channel') 35 | await channel.edit(name=new_name, user_limit=0) 36 | elif activated_checklist(channel): 37 | updated_name = name_generator(channel) 38 | await channel.edit(name=updated_name) 39 | 40 | 41 | async def channel_left(member, channel): 42 | if activated_checklist(channel): 43 | if len(channel.members) == 0: 44 | await channel.delete(reason=str(member) + ' left activated channel') 45 | else: 46 | updated_name = name_generator(channel) 47 | await channel.edit(name=updated_name) 48 | 49 | 50 | def activator_checklist(channel): 51 | if channel is None: 52 | return False 53 | afk_vc = channel.guild.afk_channel 54 | if channel.bitrate == 64000 and channel.user_limit == 1 and channel.name == 'Join to Talk': 55 | if len(channel.members) != 1: 56 | return False 57 | elif afk_vc is None: 58 | return True 59 | elif channel.id != afk_vc.id: 60 | return True 61 | return False 62 | 63 | 64 | def activated_checklist(channel): 65 | if channel is None: 66 | return False 67 | if channel.bitrate == 64000 and channel.user_limit != 1 and channel.name != 'Join to Talk' and channel.category_id == 588935560400338964: 68 | return True 69 | return False 70 | 71 | 72 | @commands.Cog.listener() 73 | async def on_voice_state_update(member, before, after): 74 | await channel_left(member, before.channel) 75 | await channel_joined(member, after.channel) 76 | 77 | 78 | @commands.Cog.listener() 79 | async def on_member_update(before, after): 80 | if before.activities != after.activities: 81 | if after.voice is not None: 82 | if activated_checklist(after.voice.channel): 83 | updated_name = name_generator(after.voice.channel) 84 | await after.voice.channel.edit(name=updated_name) 85 | 86 | 87 | def setup(bot): 88 | bot.add_listener(on_voice_state_update) 89 | bot.add_listener(on_member_update) 90 | -------------------------------------------------------------------------------- /general/coronavirus.py: -------------------------------------------------------------------------------- 1 | """ 2 | Get coronavirus data 3 | Copyright (C) 2019 Nathan Harris 4 | """ 5 | import discord 6 | from discord.ext import commands, tasks 7 | import requests 8 | from datetime import datetime 9 | 10 | 11 | def get_data(): 12 | json = requests.get('https://services1.arcgis.com/0MSEUqKaxRlEPj5g/arcgis/rest/services/ncov_cases/FeatureServer' 13 | '/2/query?f=json&where=1%3D1&returnGeometry=false&spatialRel=esriSpatialRelIntersects' 14 | '&outFields=*&orderByFields=Confirmed%20desc&resultOffset=0&resultRecordCount=250&cacheHint' 15 | '=true').json() 16 | return [country['attributes'] for country in json['features']] 17 | 18 | 19 | def convert_to_length(text, length): 20 | text = str(text) 21 | print(text) 22 | print(len(text)) 23 | if length > len(text): 24 | text = text.rjust(length - len(text)) 25 | print(text) 26 | return text 27 | 28 | 29 | def make_list(data): 30 | timestamp = int(str(data[0]['Last_Update'])[:-3]) 31 | timestamp = datetime.fromtimestamp(timestamp).strftime('%B %d, %Y %H:%M') 32 | response = "Conavirus numbers (Updated {})\n\n".format(timestamp) 33 | response += "%40s|%10s|%7s|%10s\n\n" % ("Country", "Confirmed", "Death", "Recovered") 34 | for country in data: 35 | response += "%40s|%10s|%7s|%10s\n" % ( 36 | country['Country_Region'], str(country['Confirmed']), str(country['Deaths']), str(country['Recovered'])) 37 | return response 38 | 39 | 40 | class Coronavirus(commands.Cog): 41 | 42 | @commands.command(name="coronavirus", aliases=['corona', 'covid', 'covid19'], pass_content=True) 43 | async def coronavirus(self, ctx: commands.Context): 44 | """ 45 | Get global data on the coronavirus (via ArcGIS) 46 | """ 47 | data = get_data() 48 | if data: 49 | list = make_list(data) 50 | temp_list = "" 51 | for line in list.splitlines(): 52 | if len(temp_list) < 1800: 53 | temp_list += "\n" + line 54 | else: 55 | await ctx.send("```" + temp_list + "```") 56 | temp_list = "" 57 | await ctx.send("```" + temp_list + "```") 58 | else: 59 | await ctx.send("Sorry, I couldn't grab the latest numbers") 60 | 61 | @coronavirus.error 62 | async def coronavirus_error(self, ctx, error): 63 | print(error) 64 | await ctx.send("Something went wrong.") 65 | 66 | def __init__(self, bot): 67 | self.bot = bot 68 | print("Coronavirus ready to go!") 69 | 70 | 71 | def setup(bot): 72 | bot.add_cog(Coronavirus(bot)) 73 | -------------------------------------------------------------------------------- /general/speedtest.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands, tasks 3 | import asyncio 4 | import re 5 | from asyncio import ensure_future, gather, get_event_loop, sleep 6 | from collections import deque 7 | from statistics import mean 8 | from time import time 9 | 10 | from aiohttp import ClientSession 11 | 12 | MIN_DURATION = 7 13 | MAX_DURATION = 30 14 | STABILITY_DELTA = 2 15 | MIN_STABLE_MEASUREMENTS = 6 16 | 17 | total = 0 18 | done = 0 19 | sessions = [] 20 | 21 | """ Speedtest.net """ 22 | 23 | # Coming soon 24 | 25 | """ Fast.com """ 26 | 27 | 28 | async def run(msg): 29 | token = await get_token() 30 | urls = await get_urls(token) 31 | conns = await warmup(urls) 32 | future = ensure_future(measure(conns)) 33 | result = await progress(future, msg) 34 | await cleanup() 35 | return result 36 | 37 | 38 | async def get_token(): 39 | async with ClientSession() as s: 40 | resp = await s.get('https://fast.com/') 41 | text = await resp.text() 42 | script = re.search(r'