├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── new-plugin.md ├── .gitignore ├── .scripts ├── all_data_scripts.py ├── data │ ├── anime.py │ ├── anime_tags.py │ ├── brawl_stars_brawlers.py │ ├── brawl_stars_duos_maps.py │ ├── brawl_stars_maps.py │ ├── core │ │ └── utils.py │ ├── fortnite_named_locations.py │ ├── phobias.py │ ├── pokemon_moves.py │ ├── proverbs.py │ ├── star_rail_characters.py │ ├── tft_comps.py │ ├── tft_traits.py │ ├── twice_songs.py │ ├── valorant_abilities.py │ ├── valorant_skins.py │ ├── valorant_ultimates.py │ └── wikipedia_guidelines.py ├── list_responses.py └── readme.py ├── 8ball ├── 8ball.py ├── example.png └── requirements.txt ├── LICENSE ├── README.md ├── anigame-channels └── anigame-channels.py ├── animeguesser ├── animeguesser.py └── requirements.txt ├── banana └── banana.py ├── clickthebutton ├── api.py ├── clickthebutton.py ├── data │ ├── 2022_anime.txt │ ├── agricultural_buildings.txt │ ├── anime.txt │ ├── anime_tags.txt │ ├── armed_conflicts.txt │ ├── brawl_stars_brawlers.txt │ ├── brawl_stars_duos_maps.txt │ ├── brawl_stars_maps.txt │ ├── chess_openings.txt │ ├── clash_royale_cards.txt │ ├── cobras.txt │ ├── colours.txt │ ├── commonly_misspelled_words.txt │ ├── countries.txt │ ├── dips.txt │ ├── fish.txt │ ├── fortnite_named_locations.txt │ ├── iata_codes.txt │ ├── instruments.txt │ ├── kpop_artists.txt │ ├── languages.txt │ ├── phobias.txt │ ├── pokemon_moves.txt │ ├── programming_languages.txt │ ├── proverbs.txt │ ├── skeld_locations.txt │ ├── star_rail_characters.txt │ ├── tft_comps.txt │ ├── tft_traits.txt │ ├── time_complexities.txt │ ├── tlds.txt │ ├── tone_indicators.txt │ ├── twice_songs.txt │ ├── valorant_abilities.txt │ ├── valorant_skins.txt │ ├── valorant_ultimates.txt │ ├── wikipedia_guidelines.txt │ └── words.txt ├── fonts │ └── JetBrainsMono-Regular.ttf ├── requirements.txt ├── responses.py ├── silent.py ├── stats.py ├── utils.py └── views.py ├── filebackup └── filebackup.py ├── fortniteshop └── fortniteshop.py ├── hotreload ├── hotreload.py └── requirements.txt ├── karutakeqingautoreact └── karutakeqingautoreact.py ├── marketgraph ├── marketgraph.py └── requirements.txt ├── message └── message.py ├── plugins.json ├── premiumsupport └── premiumsupport.py ├── randomvclimit └── randomvclimit.py ├── react-on-ping └── react-on-ping.py ├── remove-self-stars └── remove-self-stars.py ├── say └── say.py ├── suggest └── suggest.py ├── verification ├── captcha.png ├── requirements.txt └── verification.py └── webserver └── webserver.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: I found a bug. 4 | title: "[BUG]" 5 | labels: 'bug: maybe' 6 | assignees: RealCyGuy 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: I have idea. 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **What's your idea?** 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/new-plugin.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: New plugin 3 | about: I have new plugin idea. 4 | title: "[Plugin Request]" 5 | labels: plugin idea 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Explain your plugin:** 11 | 12 | **How is it beneficial:** 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Editors. 2 | .vscode 3 | .idea 4 | 5 | # Testing files. 6 | **/test*.* 7 | 8 | # Misc. 9 | venv 10 | 11 | *.pyc 12 | 13 | .history 14 | -------------------------------------------------------------------------------- /.scripts/all_data_scripts.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import importlib.util 3 | import os 4 | import sys 5 | from pathlib import Path 6 | 7 | os.chdir(Path(__file__).parent.joinpath("data")) 8 | sys.path.append(str(Path(__file__).parent.joinpath("data"))) 9 | for module in glob.glob("*.py"): 10 | path = Path(os.getcwd()).joinpath(module) 11 | spec = importlib.util.spec_from_file_location(path.stem, path) 12 | module = importlib.util.module_from_spec(spec) 13 | spec.loader.exec_module(module) 14 | -------------------------------------------------------------------------------- /.scripts/data/anime.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from core.utils import open_data_file 4 | 5 | url = "https://raw.githubusercontent.com/manami-project/anime-offline-database/master/anime-offline-database-minified.json" 6 | r = requests.get(url) 7 | data = r.json() 8 | 9 | with open_data_file("anime") as f: 10 | f.write(f"# {url}\n") 11 | for anime in data["data"]: 12 | if anime["type"] == "TV" and anime["episodes"] > 1: 13 | for source in anime["sources"]: 14 | if "anilist" in source: 15 | f.write(f"{source[source.rindex('/') + 1:]} {anime['title']}\n") 16 | break 17 | -------------------------------------------------------------------------------- /.scripts/data/anime_tags.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from core.utils import open_data_file 4 | 5 | url = "https://raw.githubusercontent.com/manami-project/anime-offline-database/master/anime-offline-database-minified.json" 6 | r = requests.get(url) 7 | data = r.json() 8 | 9 | with open_data_file("anime_tags") as f: 10 | f.write(f"# {url}\n") 11 | for anime in data["data"]: 12 | if anime["type"] == "TV" and anime["episodes"] > 1: 13 | for source in anime["sources"]: 14 | if "anilist" in source: 15 | f.write( 16 | f"{source[source.rindex('/') + 1:]} {','.join(anime['tags'])}\n" 17 | ) 18 | break 19 | -------------------------------------------------------------------------------- /.scripts/data/brawl_stars_brawlers.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from core.utils import open_data_file 4 | 5 | url = "https://api.brawlapi.com/v1/brawlers" 6 | r = requests.get(url) 7 | data = r.json() 8 | 9 | with open_data_file("brawl_stars_brawlers") as f: 10 | f.write(f"# {url}\n") 11 | for brawler in data["list"]: 12 | f.write(brawler["name"] + "\n") 13 | -------------------------------------------------------------------------------- /.scripts/data/brawl_stars_duos_maps.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from core.utils import open_data_file 4 | 5 | url = "https://api.brawlapi.com/v1/maps" 6 | r = requests.get(url) 7 | data = r.json() 8 | 9 | with open_data_file("brawl_stars_duos_maps") as f: 10 | f.write(f"# {url}\n") 11 | for brawl_map in data["list"]: 12 | if brawl_map["gameMode"]["hash"] == "Duo-Showdown": 13 | f.write(brawl_map["name"] + "\n") 14 | -------------------------------------------------------------------------------- /.scripts/data/brawl_stars_maps.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from core.utils import open_data_file 4 | 5 | url = "https://api.brawlapi.com/v1/maps" 6 | r = requests.get(url) 7 | data = r.json() 8 | 9 | with open_data_file("brawl_stars_maps") as f: 10 | f.write(f"# {url}\n") 11 | for brawl_map in data["list"]: 12 | if ( 13 | brawl_map["gameMode"]["hash"] != "Duo-Showdown" 14 | and not brawl_map["disabled"] 15 | ): 16 | f.write(brawl_map["name"] + "\n") 17 | -------------------------------------------------------------------------------- /.scripts/data/core/utils.py: -------------------------------------------------------------------------------- 1 | import os 2 | from typing import TextIO 3 | 4 | 5 | def open_data_file(name: str) -> TextIO: 6 | return open( 7 | os.path.join("..", "..", "clickthebutton", "data", name + ".txt"), 8 | "w", 9 | encoding="utf-8", 10 | ) 11 | 12 | 13 | def contains_swear(text: str) -> bool: 14 | return any(word in text for word in ["fuck", "bitch", "dick", "sex"]) 15 | -------------------------------------------------------------------------------- /.scripts/data/fortnite_named_locations.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from bs4 import BeautifulSoup 3 | from core.utils import open_data_file 4 | 5 | url = "https://fortnite.fandom.com/wiki/Named_Locations" 6 | r = requests.get(url) 7 | 8 | soup = BeautifulSoup(r.text, "html.parser") 9 | 10 | with open_data_file("fortnite_named_locations") as f: 11 | f.write(f"# {url}\n") 12 | for body in soup.find("div", {"class": "tabber"}).find_all("tbody"): 13 | for i, tr in enumerate(body.find_all("tr")): 14 | if i == 0: 15 | continue 16 | loc = tr.find("a").string 17 | if loc != "S1": 18 | f.write(loc + "\n") 19 | -------------------------------------------------------------------------------- /.scripts/data/phobias.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from core.utils import open_data_file 4 | 5 | url = ( 6 | "https://en.wiktionary.org/w/api.php?action=query&cmlimit=500&cmprop=title&cmtitle=Category" 7 | "%3AEnglish_terms_suffixed_with_-phobia&list=categorymembers&format=json" 8 | ) 9 | 10 | cmcontinue = None 11 | members = [] 12 | while True: 13 | if cmcontinue: 14 | r = requests.get(url + "&cmcontinue=" + cmcontinue) 15 | else: 16 | r = requests.get(url) 17 | data = r.json() 18 | for member in data["query"]["categorymembers"]: 19 | members.append(member["title"]) 20 | if "continue" not in data: 21 | break 22 | cmcontinue = data["continue"]["cmcontinue"] 23 | 24 | with open_data_file("phobias") as f: 25 | f.write(f"# {url}\n") 26 | for member in members: 27 | if member.startswith("Citations:"): 28 | continue 29 | f.write(member + "\n") 30 | -------------------------------------------------------------------------------- /.scripts/data/pokemon_moves.py: -------------------------------------------------------------------------------- 1 | from urllib.parse import urljoin 2 | 3 | import requests 4 | from bs4 import BeautifulSoup 5 | from core.utils import open_data_file 6 | 7 | url = "https://bulbapedia.bulbagarden.net/wiki/List_of_moves" 8 | r = requests.get(url) 9 | 10 | soup = BeautifulSoup(r.text, "html.parser") 11 | 12 | with open_data_file("pokemon_moves") as f: 13 | f.write(f"# {url}\n") 14 | for tr in soup.select("div#mw-content-text table table tr"): 15 | if tr.find().name == "th": 16 | continue 17 | a = tr.find("a") 18 | f.write(f"[{a.string}](<{urljoin(url, a['href'])}>)\n") 19 | -------------------------------------------------------------------------------- /.scripts/data/proverbs.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from core.utils import open_data_file, contains_swear 4 | 5 | url = "https://en.wiktionary.org/w/api.php?action=query&list=categorymembers&cmtitle=Category:English_proverbs&cmlimit=500&format=json" 6 | 7 | cmcontinue = None 8 | members = [] 9 | while True: 10 | if cmcontinue: 11 | r = requests.get(url + "&cmcontinue=" + cmcontinue) 12 | else: 13 | r = requests.get(url) 14 | data = r.json() 15 | for member in data["query"]["categorymembers"]: 16 | members.append(member["title"]) 17 | if "continue" not in data: 18 | break 19 | cmcontinue = data["continue"]["cmcontinue"] 20 | 21 | with open_data_file("proverbs") as f: 22 | f.write(f"# {url}\n") 23 | for member in members: 24 | if ":" in member or contains_swear(member): 25 | continue 26 | f.write(member + "\n") 27 | -------------------------------------------------------------------------------- /.scripts/data/star_rail_characters.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from core.utils import open_data_file 4 | 5 | url = "https://www.prydwen.gg/page-data/star-rail/characters/page-data.json" 6 | r = requests.get(url) 7 | data = r.json() 8 | 9 | with open_data_file("star_rail_characters") as f: 10 | f.write(f"# {url}\n") 11 | for character in data["result"]["data"]["allCharacters"]["nodes"]: 12 | f.write(f"{character['name']}\n") 13 | -------------------------------------------------------------------------------- /.scripts/data/tft_comps.py: -------------------------------------------------------------------------------- 1 | import requests 2 | from core.utils import open_data_file 3 | 4 | url = "https://tftacademy.com/api/tierlist/comps" 5 | r = requests.get(url) 6 | data = r.json() 7 | 8 | with open_data_file("tft_comps") as f: 9 | f.write(f"# {url}\n") 10 | for guide in data["guides"]: 11 | if guide["isPublic"]: 12 | print(guide) 13 | f.write( 14 | f"[{guide['title']}]()\n" 15 | ) 16 | -------------------------------------------------------------------------------- /.scripts/data/tft_traits.py: -------------------------------------------------------------------------------- 1 | from urllib.parse import urljoin 2 | 3 | import requests 4 | from bs4 import BeautifulSoup 5 | from core.utils import open_data_file 6 | 7 | url = "https://wiki.leagueoflegends.com/en-us/Category:TFT_traits" 8 | 9 | 10 | with open_data_file("tft_traits") as f: 11 | f.write(f"# {url}\n") 12 | while url: 13 | r = requests.get(url) 14 | soup = BeautifulSoup(r.text, "html.parser") 15 | category = soup.find("div", {"id": "mw-pages"}).find( 16 | "div", {"class": "mw-content-ltr"} 17 | ) 18 | for a in category.select("a"): 19 | if a.get("href") and a.string: 20 | f.write(f"[{a.string[4:]}](<{urljoin(url, a['href'])}>)\n") 21 | next_page = category.parent.find("a", string="next page") 22 | if next_page: 23 | url = urljoin(url, next_page["href"]) 24 | else: 25 | url = None 26 | -------------------------------------------------------------------------------- /.scripts/data/twice_songs.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from core.utils import open_data_file 4 | 5 | base_url = "https://api.spotify.com/v1" 6 | token = input( 7 | "Enter Spotify access token (from https://developer.spotify.com/documentation/web-api/tutorials/getting-started#request-an-access-token): " 8 | ).strip() 9 | 10 | headers = {"Authorization": f"Bearer {token}"} 11 | 12 | album_ids = [] 13 | artist_id = "7n2Ycct7Beij7Dj7meI4X0" 14 | url = f"{base_url}/artists/{artist_id}/albums?limit=50" 15 | while True: 16 | r = requests.get(url, headers=headers) 17 | data = r.json() 18 | for album in data["items"]: 19 | album_ids.append(album["id"]) 20 | url = data["next"] 21 | if not url: 22 | break 23 | 24 | song_names = [] 25 | for i in range(0, len(album_ids), 20): 26 | ids = ",".join(album_ids[i : i + 20]) 27 | url = f"{base_url}/albums?ids={ids}" 28 | r = requests.get(url, headers=headers) 29 | data = r.json() 30 | for album in data["albums"]: 31 | for track in album["tracks"]["items"]: 32 | art = False 33 | for artist in track["artists"]: 34 | if artist["id"] == artist_id: 35 | art = True 36 | song_names.append(track["name"]) 37 | 38 | song_names = list(set(song_names)) 39 | song_names.sort() 40 | 41 | with open_data_file("twice_songs") as f: 42 | f.write(f"# {base_url}\n") 43 | for song in song_names: 44 | f.write(song + "\n") 45 | -------------------------------------------------------------------------------- /.scripts/data/valorant_abilities.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from core.utils import open_data_file 4 | 5 | url = "https://valorant-api.com/v1/agents?isPlayableCharacter=true" 6 | r = requests.get(url) 7 | data = r.json() 8 | 9 | with open_data_file("valorant_abilities") as f: 10 | f.write(f"# {url}\n") 11 | for agent in data["data"]: 12 | for ability in agent["abilities"]: 13 | if ability["slot"] != "Passive": 14 | f.write(f"{agent['displayName']}'s {ability['displayName']}\n") 15 | -------------------------------------------------------------------------------- /.scripts/data/valorant_skins.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from core.utils import open_data_file 4 | 5 | url = "https://valorant-api.com/v1/weapons/skins" 6 | r = requests.get(url) 7 | data = r.json() 8 | 9 | with open_data_file("valorant_skins") as f: 10 | f.write(f"# {url}\n") 11 | for weapon in data["data"]: 12 | display_name = weapon["displayName"] 13 | f.write(display_name + "\n") 14 | -------------------------------------------------------------------------------- /.scripts/data/valorant_ultimates.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | from core.utils import open_data_file 4 | 5 | url = "https://valorant-api.com/v1/agents?isPlayableCharacter=true" 6 | r = requests.get(url) 7 | data = r.json() 8 | 9 | with open_data_file("valorant_ultimates") as f: 10 | f.write(f"# {url}\n") 11 | for agent in data["data"]: 12 | for ability in agent["abilities"]: 13 | if ability["slot"] == "Ultimate": 14 | name = ability["displayName"] 15 | if name == "Astral Form / Cosmic Divide": 16 | name = "Cosmic Divide" 17 | f.write(name + "\n") 18 | -------------------------------------------------------------------------------- /.scripts/data/wikipedia_guidelines.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | import requests 4 | from core.utils import open_data_file 5 | 6 | url = "https://en.wikipedia.org/wiki/Wikipedia:Shortcut_index?action=raw" 7 | r = requests.get(url) 8 | 9 | with open_data_file("wikipedia_guidelines") as f: 10 | f.write(f"# {url}\n") 11 | 12 | in_target_section = False 13 | 14 | for line in r.text.splitlines(): 15 | line = line.strip() 16 | 17 | if line == "== Procedures, policies, and guidelines ==": 18 | in_target_section = True 19 | continue 20 | 21 | if in_target_section and line.startswith("== "): 22 | in_target_section = False 23 | 24 | if not in_target_section: 25 | continue 26 | 27 | if line.startswith("| style"): 28 | parts = line.split("|") 29 | matches = re.findall(r"\[\[(WP:[^\]]+)\]\]", parts[-1]) 30 | 31 | for match in matches: 32 | f.write(f"{match} \n") 33 | -------------------------------------------------------------------------------- /.scripts/list_responses.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import sys 3 | from pathlib import Path 4 | 5 | sys.path.append(str(Path(__file__).parent.joinpath(".."))) 6 | 7 | from clickthebutton.responses import FOUGHT_OFF, SINGULAR_FOUGHT_OFF 8 | 9 | DYNAMIC_ONLY = True 10 | 11 | 12 | async def main(): 13 | for verb in FOUGHT_OFF + SINGULAR_FOUGHT_OFF: 14 | if type(verb) is tuple: 15 | parts = [] 16 | for part in verb: 17 | if callable(part): 18 | part = part() 19 | if asyncio.iscoroutine(part): 20 | part = await part 21 | parts.append(str(part)) 22 | else: 23 | parts.append(part) 24 | verb = "".join(parts) 25 | print(verb) 26 | elif not DYNAMIC_ONLY: 27 | print(verb) 28 | 29 | 30 | if __name__ == "__main__": 31 | asyncio.run(main()) 32 | -------------------------------------------------------------------------------- /.scripts/readme.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | import inspect 3 | import os 4 | import glob 5 | import pathlib 6 | import sys 7 | from unittest.mock import MagicMock 8 | 9 | sys.path.append("../") 10 | 11 | outputstart = [] 12 | outputend = "" 13 | 14 | for folder in glob.iglob(os.path.join("../", "*", "")): 15 | name = pathlib.Path(folder).name 16 | while True: 17 | try: 18 | i = importlib.import_module(f"{name}.{name}") 19 | except ImportError as e: 20 | sys.modules[e.name] = MagicMock() 21 | else: 22 | break 23 | cog = None 24 | 25 | from discord.ext import commands 26 | 27 | for c in dir(i): 28 | item = getattr(i, c) 29 | if item and inspect.isclass(item) and isinstance(item, commands.CogMeta): 30 | cog = item 31 | 32 | if cog is None: 33 | continue 34 | 35 | doc = cog.__doc__ 36 | if doc: 37 | doc = doc.strip() 38 | lines = doc.splitlines() 39 | lines = [line[4:] if line.startswith(" " * 4) else line for line in lines] 40 | # if len(lines) > 1: 41 | # doc = f"{lines[0]}
More details{' '.join(lines[1:])}
" 42 | # doc = " ".join(doc.split()) 43 | doc = "\n".join(lines) 44 | 45 | with open(os.path.join(folder, name + ".py"), "r", encoding="utf-8") as f: 46 | line = f.readline().strip("\n") 47 | if line.startswith("# registry: "): 48 | install = line[12:] 49 | else: 50 | install = f"realcyguy/modmail-plugins/{name}@v4" 51 | outputstart.append(f"[{name}](#{name})") 52 | outputend += ( 53 | f"### {name}\n" 54 | f"````\n{doc}\n````\n" 55 | f'Source code: [`{name}.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/{name}/{name}.py "{name} source code") \n' 56 | f"Install: `?plugins install {install}`\n" 57 | ) 58 | output = " · ".join(outputstart) + "\n" + outputend 59 | print(output) 60 | 61 | try: 62 | import pyperclip 63 | except ImportError: 64 | print("Couldn't copy to clipboard because pyperclip is not installed.") 65 | else: 66 | pyperclip.copy(output) 67 | print("Copied to clipboard.") 68 | -------------------------------------------------------------------------------- /8ball/8ball.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | import random 4 | import magic8ball 5 | 6 | 7 | class EightBall(commands.Cog): 8 | """ 9 | Ask Modmail a question and get an answer from a ever-growing list of answers. 10 | 11 | Disclaimer: These answers are jokes and should be taken as jokes. 12 | For legal advice, talk to a lawyer. 13 | For general advice, don't take it from a bot. 14 | """ 15 | 16 | def __init__(self, bot): 17 | self.bot = bot 18 | 19 | @commands.command(aliases=["ateball", "8bl"]) 20 | async def eightball(self, ctx, *, question): 21 | """ 22 | Ask Modmail 8-Ball a question and get a response! 23 | 24 | Usage: [prefix]eightball 25 | """ 26 | embed = discord.Embed( 27 | title=f"Question by {ctx.author}:", description=question, color=0x51EAFF 28 | ) 29 | embed.set_author( 30 | name="8-Ball", 31 | url="https://github.com/realcyguy/modmail-plugins/", 32 | icon_url="https://media.istockphoto.com/photos/pool-ball-picture-id491993923?k=6&m=491993923&s=612x612&w=0&h=u6SNe9jYA1ZidZ_vfU1LHpaDVNnrbUFivOKxazcrNCI=", 33 | ) 34 | embed.add_field( 35 | name="Answer:", value=random.choice(magic8ball.list), inline=False 36 | ) 37 | embed.set_footer(text="?plugin add realcyguy/modmail-plugins/8ball") 38 | await ctx.send(embed=embed) 39 | 40 | 41 | async def setup(bot): 42 | await bot.add_cog(EightBall(bot)) 43 | -------------------------------------------------------------------------------- /8ball/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RealCyGuy/modmail-plugins/b381bd1eeaeacb591d2313cf7a1744f00f5baf53/8ball/example.png -------------------------------------------------------------------------------- /8ball/requirements.txt: -------------------------------------------------------------------------------- 1 | magic8ball -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 RealCyGuy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RealCyGuy's Modmail Plugins 2 | 3 | Some [plugins](https://github.com/modmail-dev/modmail/wiki/Plugins) for the amazing Discord Modmail bot [`modmail-dev/modmail`](https://github.com/modmail-dev/modmail). 4 | 5 | [![GitHub issues](https://img.shields.io/github/issues/realcyguy/modmail-plugins?style=for-the-badge)](https://github.com/RealCyGuy/modmail-plugins/issues) 6 | [![License](https://img.shields.io/github/license/realcyguy/modmail-plugins?style=for-the-badge)](https://github.com/RealCyGuy/modmail-plugins/blob/v4/LICENSE) 7 | 8 | ## Plugins 9 | [8ball](#8ball) · [anigame-channels](#anigame-channels) · [animeguesser](#animeguesser) · [banana](#banana) · [clickthebutton](#clickthebutton) · [filebackup](#filebackup) · [fortniteshop](#fortniteshop) · [hotreload](#hotreload) · [karutakeqingautoreact](#karutakeqingautoreact) · [marketgraph](#marketgraph) · [message](#message) · [premiumsupport](#premiumsupport) · [randomvclimit](#randomvclimit) · [react-on-ping](#react-on-ping) · [remove-self-stars](#remove-self-stars) · [say](#say) · [suggest](#suggest) · [verification](#verification) · [webserver](#webserver) 10 | ### 8ball 11 | ```` 12 | Ask Modmail a question and get an answer from a ever-growing list of answers. 13 | 14 | Disclaimer: These answers are jokes and should be taken as jokes. 15 | For legal advice, talk to a lawyer. 16 | For general advice, don't take it from a bot. 17 | ```` 18 | Source code: [`8ball.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/8ball/8ball.py "8ball source code") 19 | Install: `?plugins install realcyguy/modmail-plugins/8ball@v4` 20 | ### anigame-channels 21 | ```` 22 | Automatically rename #anigame-inactive to #anigame-active during battles! 23 | ```` 24 | Source code: [`anigame-channels.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/anigame-channels/anigame-channels.py "anigame-channels source code") 25 | Install: `?plugins install realcyguy/modmail-plugins/anigame-channels@v4` 26 | ### animeguesser 27 | ```` 28 | An anime guessing game plugin featuring automatically extracted random frames from anime. 29 | Inspired by RinBot and utilizes AniList, Find My Anime, and Gojo APIs. 30 | ```` 31 | Source code: [`animeguesser.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/animeguesser/animeguesser.py "animeguesser source code") 32 | Install: `?plugins install realcyguy/modmail-plugins/animeguesser@v4` 33 | ### banana 34 | ```` 35 | Reacts with a banana emoji if someone says banana. 36 | ```` 37 | Source code: [`banana.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/banana/banana.py "banana source code") 38 | Install: `?plugins install realcyguy/modmail-plugins/banana@v4` 39 | ### clickthebutton 40 | ```` 41 | Clicking button game. Use ?startbutton to get started. 42 | ```` 43 | Source code: [`clickthebutton.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/clickthebutton/clickthebutton.py "clickthebutton source code") 44 | Install: `?plugins install realcyguy/modmail-plugins/clickthebutton@v4` 45 | ### filebackup 46 | ```` 47 | Automatically backup attachements sent in threads to a Discord channel. 48 | 49 | This is for viewing attachments in the logviewer after the thread channel has been deleted. 50 | ```` 51 | Source code: [`filebackup.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/filebackup/filebackup.py "filebackup source code") 52 | Install: `?plugins install realcyguy/modmail-plugins/filebackup@v4` 53 | ### fortniteshop 54 | ```` 55 | Receive the Fortnite item shop in a channel daily. 56 | ```` 57 | Source code: [`fortniteshop.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/fortniteshop/fortniteshop.py "fortniteshop source code") 58 | Install: `?plugins install realcyguy/modmail-plugins/fortniteshop@v4` 59 | ### hotreload 60 | ```` 61 | Hot-reload local plugins for development! 62 | ```` 63 | Source code: [`hotreload.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/hotreload/hotreload.py "hotreload source code") 64 | Install: `?plugins install realcyguy/modmail-plugins/hotreload@v4` 65 | ### karutakeqingautoreact 66 | ```` 67 | Automatically add reactions to Karuta messages for Keqing Bot that usually requires premium. 68 | ```` 69 | Source code: [`karutakeqingautoreact.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/karutakeqingautoreact/karutakeqingautoreact.py "karutakeqingautoreact source code") 70 | Install: `?plugins install realcyguy/modmail-plugins/karutakeqingautoreact@v4` 71 | ### marketgraph 72 | ```` 73 | Commands that are only useful with a specific MongoDB database schema: 74 | ```json 75 | { 76 | "date": date 77 | "item": { 78 | "name": str, 79 | "amount": int 80 | }, 81 | "price": { 82 | "name": str, 83 | "amount": int 84 | }, 85 | "rate": float 86 | } 87 | ``` 88 | ```` 89 | Source code: [`marketgraph.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/marketgraph/marketgraph.py "marketgraph source code") 90 | Install: `?plugins install realcyguy/modmail-plugins/marketgraph@v4` 91 | ### message 92 | ```` 93 | A plugin that... manages messages. 94 | 95 | It also has cool message-managing stuff. 96 | ```` 97 | Source code: [`message.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/message/message.py "message source code") 98 | Install: `?plugins install realcyguy/modmail-plugins/message@v4` 99 | ### premiumsupport 100 | ```` 101 | Special support for Premium members. 102 | ```` 103 | Source code: [`premiumsupport.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/premiumsupport/premiumsupport.py "premiumsupport source code") 104 | Install: `?plugins install realcyguy/modmail-plugins/premiumsupport@v4` 105 | ### randomvclimit 106 | ```` 107 | Automatically set a voice channel's member limit to a random value. 108 | ```` 109 | Source code: [`randomvclimit.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/randomvclimit/randomvclimit.py "randomvclimit source code") 110 | Install: `?plugins install realcyguy/modmail-plugins/randomvclimit@v4` 111 | ### react-on-ping 112 | ```` 113 | Reacts with a ping emoji when someone gets pinged. 114 | ```` 115 | Source code: [`react-on-ping.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/react-on-ping/react-on-ping.py "react-on-ping source code") 116 | Install: `?plugins install realcyguy/modmail-plugins/react-on-ping@v4` 117 | ### remove-self-stars 118 | ```` 119 | Removes self-stars for starboard. 120 | ```` 121 | Source code: [`remove-self-stars.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/remove-self-stars/remove-self-stars.py "remove-self-stars source code") 122 | Install: `?plugins install realcyguy/modmail-plugins/remove-self-stars@v4` 123 | ### say 124 | ```` 125 | A simple say command that removes everyone/here mentions. 126 | ```` 127 | Source code: [`say.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/say/say.py "say source code") 128 | Install: `?plugins install realcyguy/modmail-plugins/say@v4` 129 | ### suggest 130 | ```` 131 | Let's you send a suggestion to a designated channel. 132 | ```` 133 | Source code: [`suggest.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/suggest/suggest.py "suggest source code") 134 | Install: `?plugins install suggest` 135 | ### verification 136 | ```` 137 | Add captcha verification to your server! 138 | ```` 139 | Source code: [`verification.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/verification/verification.py "verification source code") 140 | Install: `?plugins install realcyguy/modmail-plugins/verification@v4` 141 | ### webserver 142 | ```` 143 | Runs a simple webserver on port 8080 for stuff like health checks. 144 | ```` 145 | Source code: [`webserver.py`](https://github.com/RealCyGuy/modmail-plugins/blob/v4/webserver/webserver.py "webserver source code") 146 | Install: `?plugins install realcyguy/modmail-plugins/webserver@v4` 147 | 148 | ## Contributors 149 | 150 | ![Contributors](https://contributors-img.firebaseapp.com/image?repo=realcyguy/modmail-plugins) 151 | 152 | ## License 153 | 154 | [MIT License](https://github.com/RealCyGuy/modmail-plugins/blob/v4/LICENSE) 155 | -------------------------------------------------------------------------------- /anigame-channels/anigame-channels.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | 4 | 5 | class AnigameChannels(commands.Cog): 6 | """ 7 | Automatically rename #anigame-inactive to #anigame-active during battles! 8 | """ 9 | 10 | def __init__(self, bot): 11 | self.bot = bot 12 | 13 | @commands.Cog.listener() 14 | async def on_message(self, message: discord.Message): 15 | if ( 16 | message.author.id == 571027211407196161 17 | and message.embeds 18 | and message.channel.name in ["anigame-inactive", "anigame-active"] 19 | ): 20 | embed = message.embeds[0] 21 | if embed.title.startswith("**__Challenging "): 22 | await message.channel.edit(name="anigame-active") 23 | elif embed.title.startswith("**Victory") or embed.title.startswith( 24 | "**Defeated" 25 | ): 26 | await message.channel.edit(name="anigame-inactive") 27 | 28 | 29 | def setup(bot): 30 | bot.add_cog(AnigameChannels(bot)) 31 | -------------------------------------------------------------------------------- /animeguesser/animeguesser.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import difflib 3 | import io 4 | import random 5 | from collections import Counter 6 | from datetime import datetime 7 | from typing import List, Optional, Set 8 | 9 | import aiohttp 10 | import discord 11 | import ffmpeg 12 | import m3u8 13 | from discord.ext import commands, tasks 14 | 15 | from bot import ModmailBot 16 | from core import checks 17 | from core.models import PermissionLevel, getLogger 18 | 19 | logger = getLogger(__name__) 20 | 21 | query = """ 22 | query ($page: Int) { 23 | Page(perPage: 1, page: $page) { 24 | media(type: ANIME, popularity_greater: 10000, status_not: NOT_YET_RELEASED, sort: TRENDING_DESC, isAdult: false, genre_not_in: ["Ecchi"]) { 25 | title { 26 | romaji 27 | english 28 | native 29 | userPreferred 30 | } 31 | synonyms 32 | id 33 | coverImage { 34 | extraLarge 35 | color 36 | } 37 | siteUrl 38 | startDate { 39 | year 40 | month 41 | day 42 | } 43 | format 44 | } 45 | } 46 | } 47 | """ 48 | anilist_api = "https://graphql.anilist.co" 49 | gojo_api = "https://api.gojo.wtf" 50 | find_my_anime_api = "https://find-my-anime.dtimur.de" 51 | formats = { 52 | "TV": "TV series", 53 | "TV_SHORT": "TV short", 54 | "MOVIE": "Movie", 55 | "SPECIAL": "Special", 56 | "OVA": "Original video animation", 57 | "ONA": "Original net animation", 58 | "MUSIC": "Music video", 59 | } 60 | embed_colour = 0x2B2D31 61 | anidb_api = "http://api.anidb.net:9001/httpapi?client=hello&clientver=1&protover=1&request=anime&aid=" 62 | 63 | 64 | def simplified_titles(title: str) -> Set[str]: 65 | title = title.lower() 66 | titles = {title} 67 | if len(title) > 10: 68 | titles.add(title.split("/")[0]) 69 | titles.add(title.split(":")[0]) 70 | titles.add(title.split("-")[0]) 71 | for extra_word in [ 72 | "movie", 73 | "ova", 74 | "series", 75 | "special", 76 | "ona", 77 | "season", 78 | "tv", 79 | "film", 80 | ]: 81 | titles.add(title.replace(extra_word, "")) 82 | clean_titles = set() 83 | for title in titles: 84 | clean_titles.add(title.strip().replace(" ", " ")) 85 | return clean_titles 86 | 87 | 88 | class AnimeGuesser(commands.Cog): 89 | """ 90 | An anime guessing game plugin featuring automatically extracted random frames from anime. 91 | Inspired by RinBot and utilizes AniList, Find My Anime, and Gojo APIs. 92 | """ 93 | 94 | def __init__(self, bot: ModmailBot): 95 | self.bot = bot 96 | self.db = bot.plugin_db.get_partition(self) 97 | 98 | self.active_channels: Set[int] = set() 99 | 100 | self.add_anime_loop.start() 101 | 102 | async def cog_load(self): 103 | await self.db.create_index("created_at", expireAfterSeconds=604800) 104 | 105 | def cog_unload(self): 106 | self.add_anime_loop.cancel() 107 | 108 | @checks.has_permissions(PermissionLevel.REGULAR) 109 | @commands.command(aliases=["ag"]) 110 | async def animeguesser(self, ctx: commands.Context): 111 | """Start a round of anime guesser.""" 112 | combined_id = int(f"{ctx.guild.id}{ctx.channel.id}") 113 | if combined_id in self.active_channels: 114 | return await ctx.send("There is already a round active in this channel.") 115 | 116 | round_data = await self.db.find_one_and_delete({}) 117 | if not round_data: 118 | return await ctx.send( 119 | f"No round data. If this persists, ffmpeg might not be installed. Run `{self.bot.prefix}ffmpeg` for more info." 120 | ) 121 | 122 | self.active_channels.add(combined_id) 123 | 124 | embed = discord.Embed( 125 | colour=embed_colour, description="Round starting in 5 seconds..." 126 | ) 127 | await ctx.send(embed=embed) 128 | await asyncio.sleep(5) 129 | 130 | answers = set() 131 | for answer in round_data["answers"]: 132 | answers.update(simplified_titles(answer)) 133 | 134 | def check(m: discord.Message) -> bool: 135 | if m.channel != ctx.channel or m.author.bot: 136 | return False 137 | for answer in answers: 138 | seq = difflib.SequenceMatcher(None, m.content.lower(), answer) 139 | if seq.ratio() > 0.8: 140 | return True 141 | return False 142 | 143 | async def wait_for_answer() -> discord.Message: 144 | try: 145 | msg = await self.bot.wait_for("message", check=check) 146 | return msg 147 | except asyncio.CancelledError: 148 | raise 149 | 150 | wait_task = asyncio.create_task(wait_for_answer()) 151 | hint_loop = asyncio.create_task(self.hint_loop(ctx, round_data, wait_task)) 152 | 153 | winner: Optional[discord.Message] = None 154 | try: 155 | winner = await wait_task 156 | content = f"{winner.author.mention} guessed the anime correctly!" 157 | except asyncio.CancelledError: 158 | content = "No one guessed the anime correctly!" 159 | hint_loop.cancel() 160 | 161 | colour = ( 162 | int(round_data["colour"][1:], 16) if round_data["colour"] else embed_colour 163 | ) 164 | embed = discord.Embed( 165 | colour=colour, 166 | description=f"{formats.get(round_data['format'], round_data['format'])} released on .", 167 | ) 168 | embed.set_image(url=round_data["cover_image"]) 169 | embed.set_author(name=round_data["title"], url=round_data["url"]) 170 | embed.add_field(name="Answers", value="\n".join(round_data["answers"])) 171 | send = winner.reply if winner else ctx.send 172 | await send( 173 | content=content, 174 | embed=embed, 175 | allowed_mentions=discord.AllowedMentions.none(), 176 | ) 177 | self.active_channels.remove(combined_id) 178 | 179 | @animeguesser.error 180 | async def animeguesser_error(self, ctx: commands.Context, error: Exception): 181 | combined_id = int(f"{ctx.guild.id}{ctx.channel.id}") 182 | if combined_id in self.active_channels: 183 | self.active_channels.remove(combined_id) 184 | 185 | async def hint_loop( 186 | self, 187 | ctx: commands.Context, 188 | round_data: dict, 189 | wait: asyncio.Task, 190 | ): 191 | hint = 0 192 | random.shuffle(round_data["images"]) 193 | 194 | hidden_title = "" 195 | hidden_characters = [] 196 | for i, character in enumerate(round_data["title"]): 197 | if character == " ": 198 | hidden_title += " " 199 | else: 200 | hidden_title += "_" 201 | hidden_characters.append(i) 202 | title_length = len(hidden_characters) 203 | 204 | async def reveal_characters(number: int) -> None: 205 | nonlocal hidden_title 206 | nonlocal hidden_characters 207 | for _ in range(number): 208 | index = random.choice(hidden_characters) 209 | hidden_characters.remove(index) 210 | hidden_title = list(hidden_title) 211 | hidden_title[index] = round_data["title"][index] 212 | hidden_title = "".join(hidden_title) 213 | embed = discord.Embed(colour=embed_colour, description=f"`{hidden_title}`") 214 | await ctx.send(embed=embed) 215 | await asyncio.sleep(2) 216 | 217 | for image in round_data["images"]: 218 | if len(image) == 0: 219 | continue 220 | hint += 1 221 | embed = discord.Embed(colour=embed_colour) 222 | embed.set_author(name=f"Hint {hint}") 223 | embed.set_image(url="attachment://image.png") 224 | await ctx.send( 225 | embed=embed, file=discord.File(io.BytesIO(image), filename="image.png") 226 | ) 227 | await asyncio.sleep(random.randint(0, 5)) 228 | 229 | if hint == 7: 230 | await reveal_characters(int(title_length / 18)) 231 | elif hint == 10: 232 | if title_length >= 11: 233 | await reveal_characters(int(title_length / 11)) 234 | elif hint == 13: 235 | await reveal_characters(max(int(title_length / 9), 1)) 236 | elif hint == 16: 237 | if title_length >= 7: 238 | await reveal_characters(int(title_length / 7)) 239 | elif hint == 19: 240 | await reveal_characters(max(int(title_length / 2), 1)) 241 | 242 | embed = discord.Embed(colour=embed_colour, description="5 seconds left!") 243 | await ctx.send(embed=embed) 244 | await asyncio.sleep(5) 245 | wait.cancel() 246 | 247 | @tasks.loop(minutes=2) 248 | async def add_anime_loop(self): 249 | try: 250 | while await self.db.count_documents({}) < 10: 251 | await self.add_anime() 252 | 253 | if await self.db.count_documents({}) < 100: 254 | await self.add_anime() 255 | except Exception: 256 | logger.exception( 257 | "add_anime_loop exception! waiting 1 minute before resuming loop" 258 | ) 259 | await asyncio.sleep(60) 260 | 261 | async def add_anime(self): 262 | async with aiohttp.ClientSession() as session: 263 | async with session.post( 264 | anilist_api, 265 | json={"query": query, "variables": {"page": random.randint(1, 1000)}}, 266 | ) as resp: 267 | anilist_data = (await resp.json())["data"]["Page"]["media"][0] 268 | anilist_id = anilist_data["id"] 269 | answers: List[str] = anilist_data["synonyms"] 270 | for title in anilist_data["title"].values(): 271 | if title not in answers and title is not None: 272 | answers.append(title) 273 | cover_image = anilist_data["coverImage"]["extraLarge"] 274 | colour = anilist_data["coverImage"]["color"] 275 | title = anilist_data["title"]["english"] 276 | anilist_url = anilist_data["siteUrl"] 277 | start_date = int( 278 | datetime( 279 | anilist_data["startDate"]["year"], 280 | anilist_data["startDate"]["month"], 281 | anilist_data["startDate"]["day"], 282 | ).timestamp() 283 | ) 284 | anime_format = anilist_data["format"] 285 | async with session.get( 286 | find_my_anime_api 287 | + "/api?provider=Anilist&includeAdult=true&collectionConsent=false&id=" 288 | + str(anilist_id) 289 | ) as resp: 290 | if resp.ok: 291 | fma_data = await resp.json() 292 | if fma_data: 293 | for answer in fma_data[0]["synonyms"]: 294 | if answer not in answers: 295 | answers.append(answer) 296 | else: 297 | logger.warning("Find My Anime API returned a non-200 status code!") 298 | async with session.get( 299 | gojo_api + "/episodes?id=" + str(anilist_id) 300 | ) as resp: 301 | if not resp.ok: 302 | raise Exception("gojo api response was not okay.") 303 | gojo_data = await resp.json() 304 | episodes_data = gojo_data[0]["episodes"] 305 | provider_id = gojo_data[0]["providerId"] 306 | if len(episodes_data) == 0: 307 | return 308 | episodes = Counter() 309 | for episode in random.choices(episodes_data, k=20): 310 | episodes[(episode["id"], episode["number"])] += 1 311 | images: List[bytes] = [] 312 | for episode, count in episodes.items(): 313 | async with session.get( 314 | gojo_api 315 | + f"/tiddies?provider={provider_id}&id={anilist_id}&num={episode[1]}&subType=sub&watchId={episode[0]}" 316 | ) as resp: 317 | episode_data = await resp.json() 318 | m3u8_url = episode_data["sources"][0]["url"] 319 | async with session.get(m3u8_url) as resp: 320 | variant_m3u8 = m3u8.loads(await resp.text()) 321 | variant_m3u8.base_uri = m3u8_url[: m3u8_url.rfind("/") + 1] 322 | if len(variant_m3u8.playlists) == 0: 323 | logger.warning( 324 | f"{m3u8_url} has 0 playlists! Anilist ID: {anilist_id} Skipping..." 325 | ) 326 | return 327 | lowest_bandwidth = 0 328 | lowest_bandwidth_playlist = None 329 | for playlist in variant_m3u8.playlists: 330 | playlist: m3u8.Playlist = playlist 331 | if ( 332 | lowest_bandwidth == 0 333 | or playlist.stream_info.bandwidth < lowest_bandwidth 334 | ): 335 | lowest_bandwidth = playlist.stream_info.bandwidth 336 | lowest_bandwidth_playlist = playlist.absolute_uri 337 | async with session.get(lowest_bandwidth_playlist) as resp: 338 | m3u8_obj = m3u8.loads(await resp.text()) 339 | m3u8_obj.base_uri = lowest_bandwidth_playlist[ 340 | : lowest_bandwidth_playlist.rfind("/") + 1 341 | ] 342 | for segment in random.choices(m3u8_obj.segments, k=count): 343 | async with session.get(segment.absolute_uri) as resp_2: 344 | input_stream = await resp_2.read() 345 | random_frame_time = random.uniform(0, segment.duration) 346 | random_frame_time = max(0.0, random_frame_time - 0.5) 347 | result, _ = ( 348 | ffmpeg.input("pipe:") 349 | .output( 350 | "pipe:", 351 | ss=random_frame_time, 352 | vframes=1, 353 | format="image2", 354 | ) 355 | .run( 356 | input=input_stream, 357 | capture_stdout=True, 358 | capture_stderr=True, 359 | ) 360 | ) 361 | images.append(result) 362 | await self.db.insert_one( 363 | { 364 | "answers": answers, 365 | "images": images, 366 | "cover_image": cover_image, 367 | "colour": colour, 368 | "title": title, 369 | "url": anilist_url, 370 | "timestamp": start_date, 371 | "format": anime_format, 372 | "created_at": datetime.utcnow(), 373 | } 374 | ) 375 | 376 | @checks.has_permissions(PermissionLevel.ADMIN) 377 | @commands.group(invoke_without_command=True) 378 | async def ffmpeg(self, ctx: commands.Context): 379 | """Instructions on how to install FFmpeg.""" 380 | embed = discord.Embed( 381 | colour=embed_colour, 382 | description="This plugin requires [FFmpeg](https://en.wikipedia.org/wiki/FFmpeg) to be installed and in path for download anime images!\n\n" 383 | "To install FFmpeg, you can download it from [their website](https://ffmpeg.org/download.html).\n" 384 | f"Or you can try `{self.bot.prefix}ffmpeg apt-get` to try installing with apt-get.", 385 | ) 386 | await ctx.send(embed=embed) 387 | 388 | @checks.has_permissions(PermissionLevel.ADMIN) 389 | @ffmpeg.command(name="apt-get") 390 | async def ffmpeg_apt_get(self, ctx: commands.Context): 391 | """Install FFmpeg with apt-get.""" 392 | proc = await asyncio.create_subprocess_shell( 393 | "apt-get update", 394 | stdout=asyncio.subprocess.PIPE, 395 | stderr=asyncio.subprocess.PIPE, 396 | ) 397 | 398 | stdout, stderr = await proc.communicate() 399 | 400 | proc2 = await asyncio.create_subprocess_shell( 401 | "apt-get install ffmpeg -y", 402 | stdout=asyncio.subprocess.PIPE, 403 | stderr=asyncio.subprocess.PIPE, 404 | ) 405 | 406 | stdout2, stderr2 = await proc2.communicate() 407 | 408 | embeds = [ 409 | discord.Embed( 410 | colour=embed_colour, 411 | description="View the [source code](https://github.com/RealCyGuy/modmail-plugins/blob/v4/animeguesser/animeguesser.py) to debug this more.", 412 | ) 413 | ] 414 | embeds[0].set_author(name="FFmpeg apt-get installation output") 415 | embeds[0].add_field( 416 | name="apt-get update", 417 | value=f"Output:\n```\u200b{stdout.decode()}```Errors:\n```\u200b{stderr.decode()}```", 418 | ) 419 | embeds.append(discord.Embed(colour=embed_colour)) 420 | embeds[1].add_field( 421 | name="apt-get install ffmpeg", 422 | value=f"Output:\n```\u200b{stdout2.decode()}```Errors:\n```\u200b{stderr2.decode()}```", 423 | ) 424 | for embed in embeds: 425 | await ctx.send(embed=embed) 426 | 427 | 428 | async def setup(bot): 429 | await bot.add_cog(AnimeGuesser(bot)) 430 | -------------------------------------------------------------------------------- /animeguesser/requirements.txt: -------------------------------------------------------------------------------- 1 | m3u8==3.5.0 2 | ffmpeg-python 3 | -------------------------------------------------------------------------------- /banana/banana.py: -------------------------------------------------------------------------------- 1 | from discord.ext import commands 2 | 3 | 4 | class Banana(commands.Cog): 5 | """Reacts with a banana emoji if someone says banana.""" 6 | 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @commands.Cog.listener() 11 | async def on_message(self, message): 12 | if "BANANA" in message.content.upper(): 13 | await message.add_reaction("\N{BANANA}") 14 | 15 | 16 | async def setup(bot): 17 | await bot.add_cog(Banana(bot)) 18 | -------------------------------------------------------------------------------- /clickthebutton/api.py: -------------------------------------------------------------------------------- 1 | import random 2 | import re 3 | from typing import Tuple 4 | 5 | import aiohttp 6 | 7 | 8 | async def request_oeis() -> Tuple[str, str]: 9 | async with aiohttp.ClientSession() as session: 10 | async with session.get( 11 | "https://oeis.org/webcam", 12 | params={"fromjavascript": 1, "q": "", "random": random.random()}, 13 | ) as resp: 14 | text = await resp.text() 15 | sequence = re.search(r"(?<=
).*(?=
)", text).group() 16 | oeis_id = re.search(r'(?<=href="/).*(?=">)', text).group() 17 | return sequence, oeis_id 18 | -------------------------------------------------------------------------------- /clickthebutton/clickthebutton.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import math 3 | import os 4 | import random 5 | import sys 6 | import time 7 | import uuid 8 | from collections import OrderedDict 9 | 10 | import discord 11 | import motor.motor_asyncio 12 | from discord.ext import commands 13 | from matplotlib import font_manager 14 | 15 | from core import checks 16 | from core.models import PermissionLevel 17 | 18 | from .responses import random_cooldown_over, random_divider 19 | from .stats import Stats 20 | from .utils import event 21 | from .views import PersistentView 22 | 23 | 24 | class ClickTheButton(commands.Cog): 25 | """ 26 | Clicking button game. Use ?startbutton to get started. 27 | """ 28 | 29 | def __init__(self, bot): 30 | self.bot = bot 31 | self.db: motor.motor_asyncio.AsyncIOMotorCollection = bot.api.db.plugins[ 32 | "ClickTheButton2" 33 | ] 34 | self.dbGraph: motor.motor_asyncio.AsyncIOMotorCollection = bot.api.db.plugins[ 35 | "ClickTheButton2Graph" 36 | ] 37 | self.view = None 38 | self.message = None 39 | self.leaderboard = {} 40 | self.custom_id = "" 41 | self.clickers = OrderedDict() 42 | self.streak = [] 43 | self.delete_messages = {} 44 | 45 | self.winner_role_id = 0 46 | 47 | def delete_after(self, message: discord.Message, delay: int): 48 | self.delete_messages[message.id] = message 49 | 50 | async def delete(delay: int): 51 | await asyncio.sleep(delay) 52 | try: 53 | await message.delete() 54 | except discord.HTTPException: 55 | pass 56 | del self.delete_messages[message.id] 57 | 58 | asyncio.create_task(delete(delay)) 59 | 60 | def get_sorted_leaderboard(self): 61 | return sorted(self.leaderboard.items(), key=lambda x: x[1], reverse=True) 62 | 63 | async def create_leaderboard_embed(self, cooldown=0): 64 | embed = discord.Embed( 65 | title="Click the button leaderboard!", 66 | description="Press the button that has a random global cooldown! Everytime you press it, you get one " 67 | "click (point).\n\n", 68 | colour=random.randint(0, 16777215), 69 | ) 70 | sorted_leaderboard = self.get_sorted_leaderboard() 71 | leaderboard_text = "" 72 | total_clicks = sum(self.leaderboard.values()) 73 | for n in range(1, 11): 74 | stats = "" 75 | if len(sorted_leaderboard) >= n: 76 | user = sorted_leaderboard[n - 1] 77 | stats = f"<@{user[0]}> - {user[1]} click{'s' if user[1] > 1 else ''} ({(user[1] / total_clicks * 100):.2f}%)" 78 | leaderboard_text += str(n) + ". " + stats + "\n" 79 | leaderboard_text += "\n" 80 | t = math.floor(time.time()) 81 | if cooldown: 82 | timestamp = t + cooldown + 1 83 | leaderboard_text += ( 84 | f"The button will be re-enabled ()!" 85 | ) 86 | else: 87 | leaderboard_text += ( 88 | f"You can click the button! (You could've since .)" 89 | ) 90 | embed.description += leaderboard_text 91 | players = len(self.leaderboard) 92 | divider = random_divider() 93 | embed.set_footer( 94 | text=f"{players} player{'' if players == 1 else 's'} {divider} {total_clicks} total clicks {divider} by cyrus yip" 95 | ) 96 | return embed 97 | 98 | async def cog_load(self): 99 | for font in font_manager.findSystemFonts( 100 | os.path.join(os.path.dirname(os.path.realpath(__file__)), "fonts") 101 | ): 102 | font_manager.fontManager.addfont(font) 103 | if self.view is None: 104 | config = await self.db.find_one({"_id": "config"}) 105 | data = await self.db.find_one({"_id": "data"}) or {} 106 | self.leaderboard = data.get("leaderboard", {}) 107 | if config: 108 | self.winner_role_id = config.get("winner_role", 0) 109 | self.custom_id = config.get("custom_id") 110 | if self.custom_id: 111 | self.view = PersistentView(self) 112 | self.bot.add_view(self.view) 113 | try: 114 | self.message = ( 115 | await self.bot.get_guild(config["message"][0]) 116 | .get_channel(config["message"][1]) 117 | .fetch_message(config["message"][2]) 118 | ) 119 | await self.message.edit( 120 | embed=await self.create_leaderboard_embed(), view=self.view 121 | ) 122 | asyncio.create_task( 123 | self.message.channel.send( 124 | random_cooldown_over( 125 | Stats( 126 | self.streak, 127 | self.leaderboard, 128 | self.get_sorted_leaderboard(), 129 | ) 130 | ), 131 | delete_after=0, 132 | allowed_mentions=discord.AllowedMentions.none(), 133 | ) 134 | ) 135 | except: 136 | pass 137 | streak_data = await self.db.find_one({"_id": "streak"}) 138 | if streak_data: 139 | self.streak = [streak_data["id"], streak_data["streak"]] 140 | 141 | async def cog_unload(self): 142 | if self.view: 143 | self.view.stop() 144 | tasks = [message.delete() for message in self.delete_messages.values()] 145 | await asyncio.gather(*tasks, return_exceptions=True) 146 | for module in list(sys.modules.keys()): 147 | if module.startswith( 148 | ".".join(__name__.split(".")[:-1]) 149 | ) and not module.endswith("clickthebutton"): 150 | try: 151 | del sys.modules[module] 152 | except: 153 | pass 154 | for task in asyncio.all_tasks(): 155 | if self.view.id in task.get_name(): 156 | try: 157 | task.cancel() 158 | except: 159 | pass 160 | if self.streak: 161 | await self.db.update_one( 162 | {"_id": "streak"}, 163 | {"$set": {"id": self.streak[0], "streak": self.streak[1]}}, 164 | upsert=True, 165 | ) 166 | 167 | @checks.has_permissions(PermissionLevel.ADMIN) 168 | @commands.command() 169 | async def startbutton(self, ctx: commands.Context): 170 | """Starts a persistent view.""" 171 | self.custom_id = str(uuid.uuid4()) 172 | if self.view: 173 | self.view.stop() 174 | self.view = PersistentView(self) 175 | msg = await ctx.send( 176 | event("Click the button leaderboard was created!"), 177 | embed=await self.create_leaderboard_embed(), 178 | view=self.view, 179 | ) 180 | await self.db.update_one( 181 | {"_id": "config"}, 182 | { 183 | "$set": { 184 | "custom_id": self.custom_id, 185 | "message": [msg.guild.id, msg.channel.id, msg.id], 186 | } 187 | }, 188 | upsert=True, 189 | ) 190 | 191 | @checks.has_permissions(PermissionLevel.OWNER) 192 | @commands.command() 193 | async def reimbursecookieclicks(self, ctx: commands.Context): 194 | """ 195 | Reimburses all users for the clicks they spent on cookies. 196 | 197 | There used to be a button that gives them a cookie for 1 click, but it has been removed. 198 | This command reimburses all users for the clicks they lost. 199 | 200 | Here is the old cookie code: [github](https://github.com/RealCyGuy/modmail-plugins/blob/8803a954e7856aa5c3a30dad060e4abf90ea22af/clickthebutton/views.py#L215). 201 | """ 202 | async for user in self.db.find({"user": True}): 203 | if user.get("cookies", 0) > 0: 204 | self.leaderboard[str(user["id"])] += user["cookies"] 205 | await ctx.send( 206 | f"Reimbursing <@{user['id']}> with {user['cookies']} click(s). Updated clicks: {self.leaderboard[str(user['id'])]}." 207 | ) 208 | 209 | @checks.has_permissions(PermissionLevel.ADMIN) 210 | @commands.command() 211 | async def setwinnerrole(self, ctx, role_id: int): 212 | """ 213 | Set role for first place. Set to 0 to be none. 214 | """ 215 | self.winner_role_id = role_id 216 | await self.db.update_one( 217 | {"_id": "config"}, 218 | { 219 | "$set": { 220 | "winner_role": self.winner_role_id, 221 | } 222 | }, 223 | upsert=True, 224 | ) 225 | await ctx.send(f"Winner role id set to `{self.winner_role_id}`") 226 | 227 | 228 | async def setup(bot): 229 | await bot.add_cog(ClickTheButton(bot)) 230 | -------------------------------------------------------------------------------- /clickthebutton/data/2022_anime.txt: -------------------------------------------------------------------------------- 1 | # https://en.wikipedia.org/wiki/2022_in_anime#Television_series 2 | In the Land of Leadale 3 | Irodorimidori 4 | Police in a Pod 5 | Orient (part 1) 6 | Saiyuki Reload: Zeroin 7 | Tokyo 24th Ward 8 | Slow Loop 9 | Cue! 10 | Girls' Frontline 11 | Ninjala 12 | Teasing Master Takagi-san (season 3) 13 | The Strongest Sage With the Weakest Crest 14 | Akebi's Sailor Uniform 15 | Futsal Boys!!!!! 16 | How a Realist Hero Rebuilt the Kingdom (part 2) 17 | Miss Kuroitsu from the Monster Development Department 18 | My Dress-Up Darling 19 | Requiem of the Rose King 20 | Rusted Armors: Daybreak 21 | Attack on Titan: The Final Season (part 2) 22 | GaruGaku II: Lucky Stars 23 | On Air Dekinai! 24 | Sasaki and Miyano 25 | Tribe Nine 26 | Fantasia Sango - Realm of Legends 27 | Princess Connect! Re:Dive (season 2) 28 | Sabikui Bisco 29 | The Genius Prince's Guide to Raising a Nation Out of Debt 30 | Life with an Ordinary Guy Who Reincarnated into a Total Fantasy Knockout 31 | She Professed Herself Pupil of the Wise Man 32 | Arifureta: From Commonplace to World's Strongest (season 2) 33 | Love of Kill 34 | I'm Kodama Kawashiri 35 | The Case Study of Vanitas (part 2) 36 | Salaryman's Club 37 | Delicious Party Pretty Cure 38 | Shenmue the Animation 39 | Aharen-san wa Hakarenai 40 | Fanfare of Adolescence 41 | Love All Play 42 | Love Live! Nijigasaki High School Idol Club (season 2) 43 | Mahjong Soul Pong 44 | Science Fell in Love, So I Tried to Prove It r=1-sinθ 45 | Shadowverse Flame 46 | The Executioner and Her Way of Life 47 | Black Rock Shooter: Dawn Fall 48 | Build Divide -#FFFFFF (Code White)- 49 | Magia Record: Puella Magi Madoka Magica Side Story - Dawn of a Shallow Dream 50 | Trapped in a Dating Sim: The World of Otome Games Is Tough for Mobs 51 | Chiikawa 52 | Healer Girl 53 | Insect Land 54 | Detective Conan: Zero's Tea Time 55 | I'm Quitting Heroing 56 | Ya Boy Kongming! 57 | Birdie Wing: Golf Girls' Story 58 | Deaimon 59 | RPG Real Estate 60 | The Greatest Demon Lord Is Reborn as a Typical Nobody 61 | The Rising of the Shield Hero (season 2) 62 | Tomodachi Game 63 | Estab-Life: Great Escape 64 | Heroines Run the Show 65 | Komi Can't Communicate (season 2) 66 | Miss Shachiku and the Little Baby Ghost 67 | Skeleton Knight in Another World 68 | Date A Live IV 69 | Love After World Domination 70 | The Dawn of the Witch 71 | The Demon Girl Next Door (season 2) 72 | Aoashi 73 | Dance Dance Danseur 74 | Kaguya-sama: Love Is War – Ultra Romantic 75 | Spy × Family (part 1) 76 | Yatogame-chan Kansatsu Nikki (season 4) 77 | Don't Hurt Me, My Healer! 78 | In the Heart of Kunoichi Tsubaki 79 | Kingdom (season 4) 80 | Shikimori's Not Just a Cutie 81 | Onipan! 82 | Amaim Warrior at the Borderline (part 2) 83 | Ascendance of a Bookworm (season 3) 84 | Kaginado (season 2) 85 | Summer Time Rendering 86 | A Couple of Cuckoos 87 | Shin Ikki Tousen 88 | Iii Icecrin 2 89 | Lycoris Recoil 90 | Musasino! 91 | Phantom of the Idol 92 | Rent-A-Girlfriend (season 2) 93 | Shoot! Goal to the Future 94 | Teppen!!!!!!!!!!!!!!! Laughing 'til You Cry 95 | Engage Kiss 96 | Luminous Witches 97 | RWBY: Ice Queendom 98 | Utawarerumono: Mask of Truth 99 | Yurei Deco 100 | Classroom of the Elite (season 2) 101 | My Isekai Life 102 | Overlord (season 4) 103 | Vermeil in Gold 104 | Dropkick on My Devil! X 105 | Harem in the Labyrinth of Another World 106 | Made in Abyss: The Golden City of the Scorching Sun 107 | My Stepmom's Daughter Is My Ex 108 | Smile of the Arsnotoria the Animation 109 | Tokyo Mew Mew New 110 | Uncle from Another World 111 | Chimimo 112 | The Prince of Tennis II: U-17 World Cup 113 | The Yakuza's Guide to Babysitting 114 | Call of the Night 115 | Shine On! Bakumatsu Bad Boys! 116 | When Will Ayumu Make His Move? 117 | Black Summoner 118 | Lucifer and the Biscuit Hammer 119 | Prima Doll 120 | Shadows House (season 2) 121 | Extreme Hearts 122 | Hanabi-chan Is Often Late 123 | KJ File 124 | Parallel World Pharmacy 125 | Orient (part 2) 126 | Shine Post 127 | The Devil Is a Part-Timer!! 128 | Love Live! Superstar!! (season 2) 129 | Is It Wrong to Try to Pick Up Girls in a Dungeon? (season 4, part 1) 130 | The Maid I Hired Recently Is Mysterious 131 | BanG Dream! Morfonication 132 | Fuuto PI 133 | Nights with a Cat 134 | Chickip Dancers (season 2) 135 | My Master Has No Tail 136 | I'm the Villainess, So I'm Taming the Final Boss 137 | I've Somehow Gotten Stronger When I Improved My Farm-Related Skills 138 | My Hero Academia (season 6) 139 | Raven of the Inner Palace 140 | Spy × Family (part 2) 141 | Uzaki-chan Wants to Hang Out! ω 142 | Beast Tamer 143 | Berserk: The Golden Age Arc – Memorial Edition 144 | Harem Camp! 145 | Housing Complex C 146 | Idolish7: Third Beat! (part 2) 147 | Mobile Suit Gundam: The Witch from Mercury (part 1) 148 | Pop Team Epic (season 2) 149 | PuniRunes 150 | Golden Kamuy (season 4) 151 | Mamekichi Mameko NEET no Nichijō 152 | Management of a Novice Alchemist 153 | Detective Conan: The Culprit Hanzawa 154 | Shinobi no Ittoki 155 | Encouragement of Climb: Next Summit 156 | Immoral Guild 157 | Reincarnated as a Sword 158 | The Eminence in Shadow 159 | The Human Crazy University 160 | VazzRock the Animation 161 | Bibliophile Princess 162 | Do It Yourself!! 163 | Mob Psycho 100 III 164 | Muv-Luv Alternative (season 2) 165 | Sylvanian Families: Flare no Happy Diary 166 | Akiba Maid War 167 | Megaton Musashi (season 2) 168 | Reiwa no Di Gi Charat 169 | Legend of Mana: The Teardrop Crystal 170 | Pui Pui Molcar Driving School 171 | Welcome to Demon School! Iruma-kun (season 3) 172 | Blue Lock 173 | Bocchi the Rock! 174 | More Than a Married Couple, But Not Lovers 175 | Peter Grill and the Philosopher's Time: Super Extra 176 | Yowamushi Pedal: Limit Break 177 | Bleach: Thousand-Year Blood War (part 1) 178 | Eternal Boys 179 | Play It Cool, Guys 180 | Chainsaw Man 181 | Love Flops 182 | Aru Asa Dummy Head Mike ni Natteita Ore-kun no Jinsei 183 | Urusei Yatsura 184 | The Little Lies We All Tell 185 | To Your Eternity (season 2) 186 | Arknights: Prelude to Dawn 187 | Kantai Collection: Let's Meet at Sea 188 | Me & Roboco 189 | -------------------------------------------------------------------------------- /clickthebutton/data/agricultural_buildings.txt: -------------------------------------------------------------------------------- 1 | # https://en.wikipedia.org/wiki/List_of_building_types#Agricultural 2 | Abattoir 3 | Barn 4 | Chicken coop 5 | Cow-shed 6 | Farmhouse 7 | Granary 8 | Greenhouse 9 | Hayloft 10 | Pigpen 11 | Root cellar 12 | Shed 13 | Silo 14 | Slaughterhouse 15 | Stable 16 | Storm cellar 17 | Well house 18 | Crib 19 | Windmill 20 | Workshop 21 | -------------------------------------------------------------------------------- /clickthebutton/data/armed_conflicts.txt: -------------------------------------------------------------------------------- 1 | # https://en.wikipedia.org/wiki/List_of_anthropogenic_disasters_by_death_toll#Wars_and_armed_conflicts 2 | World War II 3 | Mongol invasions and conquests 4 | Taiping Rebellion 5 | European colonization of the Americas 6 | Transition from Ming to Qing 7 | Second Sino-Japanese War 8 | World War I 9 | An Lushan Rebellion 10 | Dungan Revolt 11 | Chinese Civil War 12 | Russian Civil War 13 | Thirty Years' War 14 | Mughal–Maratha Wars 15 | Napoleonic Wars 16 | Yellow Turban Rebellion 17 | Second Congo War 18 | French Wars of Religion 19 | Hundred Years' War 20 | Korean War 21 | Qin's wars of unification 22 | Vietnam War 23 | Crusades 24 | Nigerian Civil War 25 | Mfecane 26 | Punic Wars 27 | Second Sudanese Civil War 28 | Seven Years' War 29 | Soviet–Afghan War 30 | Japanese invasions of Korea 31 | French Revolutionary Wars 32 | Mexican Revolution 33 | Panthay Rebellion 34 | Wars of the Three Kingdoms 35 | Conquests of Mehmed II 36 | Ethiopian Civil War 37 | Jewish–Roman wars 38 | American Civil War 39 | Indian Rebellion of 1857 40 | Bangladesh Liberation War 41 | Algerian War 42 | War of the Spanish Succession 43 | Spanish Civil War 44 | Eighty Years' War 45 | Gallic Wars 46 | Spanish American wars of independence 47 | Iran–Iraq War 48 | Syrian civil war 49 | Tigray War 50 | French invasion of Russia 51 | English Civil War 52 | Angolan Civil War 53 | First Sudanese Civil War 54 | War on terror 55 | Colombian conflict 56 | Albigensian Crusade 57 | First Congo War 58 | Maratha invasions of Bengal 59 | First Indochina War 60 | Continuation War 61 | Somali Civil War 62 | South Sudanese Civil War 63 | Crimean War 64 | Cuban War of Independence 65 | Iraq War 66 | Boko Haram insurgency 67 | Great Northern War 68 | Italian Wars 69 | French conquest of Algeria 70 | Burundian Civil War 71 | Yemeni Civil War (2014–present) 72 | War in Darfur 73 | Second Italo-Ethiopian War 74 | Paraguayan War 75 | Papua conflict 76 | Ten Years' War 77 | Kalinga War 78 | Philippine–American War 79 | Venezuelan War of Independence 80 | Ugandan Bush War 81 | Lord's Resistance Army insurgency 82 | Franco-Dutch War 83 | War in Iraq (2013-2017) 84 | Iraqi–Kurdish conflict 85 | Campaigns of Suleiman the Magnificent 86 | Franco-Spanish War (1635–1659) 87 | Carlist Wars 88 | La Violencia 89 | War in Afghanistan (2001-2021) 90 | Internal conflict in Myanmar 91 | Winter War 92 | Guatemalan Civil War 93 | Greek Civil War 94 | North Yemen Civil War 95 | 1991 Iraqi uprisings 96 | Balkan Wars 97 | Anglo-Spanish War (1585–1604) 98 | Saint-Domingue Expedition 99 | Yugoslav Wars 100 | Lebanese Civil War 101 | Sierra Leone Civil War 102 | Great Turkish War 103 | Thousand Days' War 104 | Moro conflict 105 | 2022 Russian invasion of Ukraine 106 | Arab–Israeli conflict 107 | Mexican drug war 108 | Aceh War 109 | Bosnian War 110 | German Peasants' War 111 | Kurdish rebellions in Turkey 112 | Congo Crisis 113 | Insurgency in Laos 114 | Kivu conflict 115 | Kashmir conflict 116 | Algerian Civil War 117 | Angolan War of Independence 118 | Sri Lankan Civil War 119 | Annexation of Hyderabad 120 | -------------------------------------------------------------------------------- /clickthebutton/data/brawl_stars_brawlers.txt: -------------------------------------------------------------------------------- 1 | # https://api.brawlapi.com/v1/brawlers 2 | Ollie 3 | Meeple 4 | Buzz Lightyear 5 | Juju 6 | Shade 7 | Kenji 8 | Moe 9 | Clancy 10 | Berry 11 | Lily 12 | Draco 13 | Angelo 14 | Melodie 15 | Larry & Lawrie 16 | Kit 17 | Mico 18 | Charlie 19 | Chuck 20 | Pearl 21 | Doug 22 | Cordelius 23 | Hank 24 | Maisie 25 | Willow 26 | R-T 27 | Mandy 28 | Gray 29 | Chester 30 | Buster 31 | Gus 32 | Sam 33 | Otis 34 | Bonnie 35 | Janet 36 | Eve 37 | Fang 38 | Lola 39 | Meg 40 | Ash 41 | Griff 42 | Buzz 43 | Grom 44 | Squeak 45 | Belle 46 | Stu 47 | Ruffs 48 | Edgar 49 | Byron 50 | Lou 51 | Amber 52 | Colette 53 | Surge 54 | Sprout 55 | Nani 56 | Gale 57 | Jacky 58 | Max 59 | Mr. P 60 | Emz 61 | Bea 62 | Sandy 63 | 8-Bit 64 | Bibi 65 | Carl 66 | Rosa 67 | Leon 68 | Tick 69 | Gene 70 | Frank 71 | Penny 72 | Darryl 73 | Tara 74 | Pam 75 | Piper 76 | Bo 77 | Poco 78 | Crow 79 | Mortis 80 | El Primo 81 | Dynamike 82 | Nita 83 | Jessie 84 | Barley 85 | Spike 86 | Rico 87 | Brock 88 | Bull 89 | Colt 90 | Shelly 91 | -------------------------------------------------------------------------------- /clickthebutton/data/brawl_stars_duos_maps.txt: -------------------------------------------------------------------------------- 1 | # https://api.brawlapi.com/v1/maps 2 | Double Trouble 3 | Skull Creek 4 | Scorched Stone 5 | Rockwall Brawl 6 | Feast Or Famine 7 | Acid Lakes 8 | Cavern Churn 9 | Island Invasion 10 | Hot Maze 11 | Dune Drift 12 | Stormy Plains 13 | Dark Passage 14 | Ghost Point 15 | Erratic Blocks 16 | River Rush 17 | Eye Of The Storm 18 | Flying Fantasies 19 | Royal Runway 20 | Superstar 21 | Deserted Vertex 22 | Outrageous Outback 23 | Barren Badlands 24 | Critical Crossing 25 | Skull Creek 26 | Hot Maze 27 | Acid Lakes 28 | Ghost Point 29 | Forsaken Falls 30 | Dark Passage 31 | Eye Of The Storm 32 | Point Of View 33 | Circular Canyon 34 | Rocky Blocks 35 | Stocky Stockades 36 | Safety Center 37 | Core Crumble 38 | Treasure Hunt 39 | Two Thousand Lakes 40 | Cavern Churn 41 | Thousand Lakes 42 | Nruhc Nrevac 43 | Riverside Ring 44 | Shadow Spirits 45 | Eggshell 46 | Will Of The Wisp 47 | Victor Valley 48 | Grassy Gorge 49 | Two Thousand Lakes 50 | Clash Colosseum 51 | Riverside Ring 52 | Shadow Spirits 53 | Eggshell 54 | Will Of The Wisp 55 | Victor Valley 56 | Grassy Gorge 57 | Clash Colosseum 58 | The Galaxy 59 | Dark Fantasies 60 | Risky Cliffs 61 | Ignition 62 | Mystic Touch 63 | Trap Map 64 | Dried Up River 65 | Canyon Rivers 66 | Canyon Rivers 67 | The Mortuary 68 | Jump Park 69 | Brawler Speedway 70 | Teleport Tag 71 | Boxed In 72 | Zoomed Out 73 | Lush Poles 74 | Hard Limits 75 | Vicious Vortex 76 | Descending Dimensions 77 | Marksmans Paradise 78 | Desert Tendrils 79 | Feast Or Famine 80 | Island Invasion 81 | Fiftyfive 82 | Leaping Frogs 83 | Final Four 84 | Sunset Vista 85 | -------------------------------------------------------------------------------- /clickthebutton/data/brawl_stars_maps.txt: -------------------------------------------------------------------------------- 1 | # https://api.brawlapi.com/v1/maps 2 | Snake Prairie 3 | Shooting Star 4 | Hard Rock Mine 5 | Crystal Arcade 6 | Deathcap Trap 7 | Gem Fort 8 | Undermine 9 | Skull Creek 10 | Rockwall Brawl 11 | Feast Or Famine 12 | Kaboom Canyon 13 | Safe Zone 14 | Hideout 15 | Backyard Bowl 16 | Triple Dribble 17 | Pinhole Punt 18 | Acid Lakes 19 | Cavern Churn 20 | Double Trouble 21 | Island Invasion 22 | Sneaky Fields 23 | Super Beach 24 | Hot Potato 25 | Canal Grande 26 | Layer Cake 27 | Dark Passage 28 | Double Swoosh 29 | Pinball Dreams 30 | Minecart Madness 31 | Flying Fantasies 32 | Center Stage 33 | Beach Ball 34 | Sunny Soccer 35 | Safety Center 36 | Penalty Kick 37 | Open Business 38 | Parallel Plays 39 | No Excuses 40 | Ring Of Fire 41 | Dueling Beetles 42 | Rustic Arcade 43 | Goldarm Gulch 44 | Belles Rock 45 | Dried Up River 46 | Flaring Phoenix 47 | No Surrender 48 | Shrouding Serpent 49 | Warriors Way 50 | Monkey Maze 51 | Infinite Doom 52 | Slayers Paradise 53 | Quad Damage 54 | Open Space 55 | Out In The Open 56 | Petticoat Duel 57 | Layer Bake 58 | Last Stop 59 | Freezing Ripples 60 | Great Waves 61 | Cool Shapes 62 | Slippery Road 63 | Icy Ice Park 64 | Frosty Tracks 65 | The Great Open 66 | Marksmans Paradise 67 | New Horizons 68 | Suspenders 69 | Riverbank Crossing 70 | Four Levels 71 | Spice Production 72 | Zen Garden 73 | Watersport 74 | Noisy Neighbors 75 | Forest Clearing 76 | The Cooler Hard Rock 77 | H For... 78 | Two Rivers 79 | Second Try 80 | Trickey 81 | Safe(r) Zone 82 | The Great Lake 83 | Deep Forest 84 | Tiny Islands 85 | Temple Of Vroom 86 | Overgrown Ruins 87 | Gg 2.0 88 | -------------------------------------------------------------------------------- /clickthebutton/data/clash_royale_cards.txt: -------------------------------------------------------------------------------- 1 | # https://statsroyale.com/top/cards 2 | # (.*\n){5}^$\n 3 | Valkyrie 4 | Mini P.E.K.K.A 5 | The Log 6 | Arrows 7 | Goblin Barrel 8 | Skeleton Army 9 | Fireball 10 | Mega Knight 11 | Witch 12 | Zap 13 | Wizard 14 | Knight 15 | Baby Dragon 16 | Electro Wizard 17 | Prince 18 | Musketeer 19 | Hog Rider 20 | Dark Prince 21 | Archers 22 | P.E.K.K.A 23 | Princess 24 | Bats 25 | Bomber 26 | Goblin Gang 27 | Inferno Dragon 28 | Cannon 29 | Ice Spirit 30 | Miner 31 | Skeletons 32 | Minions 33 | Bandit 34 | Tesla 35 | Firecracker 36 | Dart Goblin 37 | Elite Barbarians 38 | Giant 39 | Spear Goblins 40 | Inferno Tower 41 | Balloon 42 | Goblins 43 | Ram Rider 44 | Barbarian Barrel 45 | Wall Breakers 46 | Goblin Hut 47 | Mega Minion 48 | Tornado 49 | Lumberjack 50 | Barbarians 51 | Guards 52 | Sparky 53 | Magic Archer 54 | Minion Horde 55 | Tombstone 56 | Poison 57 | Rocket 58 | Archer Queen 59 | Bomb Tower 60 | Ice Golem 61 | Mirror 62 | Battle Ram 63 | Golden Knight 64 | Phoenix 65 | Fire Spirit 66 | Lightning 67 | Goblin Cage 68 | Freeze 69 | Royal Hogs 70 | Rage 71 | Golem 72 | Executioner 73 | Giant Skeleton 74 | Royal Ghost 75 | Earthquake 76 | Goblin Giant 77 | Ice Wizard 78 | Electro Spirit 79 | Skeleton Barrel 80 | Giant Snowball 81 | Royal Delivery 82 | Graveyard 83 | Flying Machine 84 | Mother Witch 85 | X-Bow 86 | Royal Giant 87 | Skeleton Dragons 88 | Elixir Collector 89 | Electro Giant 90 | Night Witch 91 | Skeleton King 92 | Furnace 93 | Elixir Golem 94 | Lava Hound 95 | Mighty Miner 96 | Clone 97 | Barbarian Hut 98 | Goblin Drill 99 | Royal Recruits 100 | Cannon Cart 101 | Hunter 102 | Rascals 103 | Bowler 104 | Heal Spirit 105 | Three Musketeers 106 | Fisherman 107 | Zappies 108 | Mortar 109 | Electro Dragon 110 | Battle Healer 111 | Super Magic Archer 112 | Santa Hog Rider 113 | Monk 114 | -------------------------------------------------------------------------------- /clickthebutton/data/cobras.txt: -------------------------------------------------------------------------------- 1 | # https://en.wikipedia.org/wiki/List_of_snakes_by_common_name#C 2 | Andaman cobra 3 | Arabian cobra 4 | Asian cobra 5 | Banded water cobra 6 | Black-necked cobra 7 | Black-necked spitting cobra 8 | Black tree cobra 9 | Burrowing cobra 10 | Cape cobra 11 | Caspian cobra 12 | Chinese cobra 13 | Cobra de capello 14 | Congo water cobra 15 | Common cobra 16 | Eastern water cobra 17 | Egyptian cobra 18 | Equatorial spitting cobra 19 | False cobra 20 | False water cobra 21 | Forest cobra 22 | Gold tree cobra 23 | Indian cobra 24 | Indochinese spitting cobra 25 | Javan spitting cobra 26 | King cobra 27 | Mandalay cobra 28 | Monocled cobra 29 | Monoculate cobra 30 | Mozambique spitting cobra 31 | North Philippine cobra 32 | Nubian spitting cobra 33 | Philippine cobra 34 | Red spitting cobra 35 | Rinkhals cobra 36 | Shield-nosed cobra 37 | Sinai desert cobra 38 | Southern Indonesian spitting cobra 39 | Southern Philippine cobra 40 | Southwestern black spitting cobra 41 | Snouted cobra 42 | Spectacled cobra 43 | Spitting cobra 44 | Storm water cobra 45 | Thai cobra 46 | Taiwan cobra 47 | West African brown spitting cobra 48 | White-lipped cobra 49 | Yellow cobra 50 | Zebra spitting cobra 51 | -------------------------------------------------------------------------------- /clickthebutton/data/colours.txt: -------------------------------------------------------------------------------- 1 | # https://en.wikipedia.org/wiki/X11_color_names#Color_name_chart 2 | Alice Blue 3 | Antique White 4 | Aqua 5 | Aquamarine 6 | Azure 7 | Beige 8 | Bisque 9 | Black 10 | Blanched Almond 11 | Blue 12 | Blue Violet 13 | Brown 14 | Burlywood 15 | Cadet Blue 16 | Chartreuse 17 | Chocolate 18 | Coral 19 | Cornflower Blue 20 | Cornsilk 21 | Crimson 22 | Cyan 23 | Dark Blue 24 | Dark Cyan 25 | Dark Goldenrod 26 | Dark Gray 27 | Dark Green 28 | Dark Khaki 29 | Dark Magenta 30 | Dark Olive Green 31 | Dark Orange 32 | Dark Orchid 33 | Dark Red 34 | Dark Salmon 35 | Dark Sea Green 36 | Dark Slate Blue 37 | Dark Slate Gray 38 | Dark Turquoise 39 | Dark Violet 40 | Deep Pink 41 | Deep Sky Blue 42 | Dim Gray 43 | Dodger Blue 44 | Firebrick 45 | Floral White 46 | Forest Green 47 | Fuchsia 48 | Gainsboro* 49 | Ghost White 50 | Gold 51 | Goldenrod 52 | Gray 53 | Web Gray 54 | Green 55 | Web Green 56 | Green Yellow 57 | Honeydew 58 | Hot Pink 59 | Indian Red 60 | Indigo 61 | Ivory 62 | Khaki 63 | Lavender 64 | Lavender Blush 65 | Lawn Green 66 | Lemon Chiffon 67 | Light Blue 68 | Light Coral 69 | Light Cyan 70 | Light Goldenrod 71 | Light Gray 72 | Light Green 73 | Light Pink 74 | Light Salmon 75 | Light Sea Green 76 | Light Sky Blue 77 | Light Slate Gray 78 | Light Steel Blue 79 | Light Yellow 80 | Lime 81 | Lime Green 82 | Linen 83 | Magenta 84 | Maroon 85 | Web Maroon 86 | Medium Aquamarine 87 | Medium Blue 88 | Medium Orchid 89 | Medium Purple 90 | Medium Sea Green 91 | Medium Slate Blue 92 | Medium Spring Green 93 | Medium Turquoise 94 | Medium Violet Red 95 | Midnight Blue 96 | Mint Cream 97 | Misty Rose 98 | Moccasin 99 | Navajo White 100 | Navy Blue 101 | Old Lace 102 | Olive 103 | Olive Drab 104 | Orange 105 | Orange Red 106 | Orchid 107 | Pale Goldenrod 108 | Pale Green 109 | Pale Turquoise 110 | Pale Violet Red 111 | Papaya Whip 112 | Peach Puff 113 | Peru 114 | Pink 115 | Plum 116 | Powder Blue 117 | Purple 118 | Web Purple 119 | Rebecca Purple 120 | Red 121 | Rosy Brown 122 | Royal Blue 123 | Saddle Brown 124 | Salmon 125 | Sandy Brown 126 | Sea Green 127 | Seashell 128 | Sienna 129 | Silver 130 | Sky Blue 131 | Slate Blue 132 | Slate Gray 133 | Snow 134 | Spring Green 135 | Steel Blue 136 | Tan 137 | Teal 138 | Thistle 139 | Tomato 140 | Turquoise 141 | Violet 142 | Wheat 143 | White 144 | White Smoke 145 | Yellow 146 | Yellow Green 147 | -------------------------------------------------------------------------------- /clickthebutton/data/countries.txt: -------------------------------------------------------------------------------- 1 | # https://gist.githubusercontent.com/kalinchernev/486393efcca01623b18d/raw/daa24c9fea66afb7d68f8d69f0c4b8eeb9406e83/countries 2 | Afghanistan 3 | Albania 4 | Algeria 5 | Andorra 6 | Angola 7 | Antigua & Deps 8 | Argentina 9 | Armenia 10 | Australia 11 | Austria 12 | Azerbaijan 13 | Bahamas 14 | Bahrain 15 | Bangladesh 16 | Barbados 17 | Belarus 18 | Belgium 19 | Belize 20 | Benin 21 | Bhutan 22 | Bolivia 23 | Bosnia Herzegovina 24 | Botswana 25 | Brazil 26 | Brunei 27 | Bulgaria 28 | Burkina 29 | Burundi 30 | Cambodia 31 | Cameroon 32 | Canada 33 | Cape Verde 34 | Central African Rep 35 | Chad 36 | Chile 37 | China 38 | Colombia 39 | Comoros 40 | Congo 41 | Costa Rica 42 | Croatia 43 | Cuba 44 | Cyprus 45 | Czech Republic 46 | Denmark 47 | Djibouti 48 | Dominica 49 | Dominican Republic 50 | East Timor 51 | Ecuador 52 | Egypt 53 | El Salvador 54 | Equatorial Guinea 55 | Eritrea 56 | Estonia 57 | Ethiopia 58 | Fiji 59 | Finland 60 | France 61 | Gabon 62 | Gambia 63 | Georgia 64 | Germany 65 | Ghana 66 | Greece 67 | Grenada 68 | Guatemala 69 | Guinea 70 | Guinea-Bissau 71 | Guyana 72 | Haiti 73 | Honduras 74 | Hungary 75 | Iceland 76 | India 77 | Indonesia 78 | Iran 79 | Iraq 80 | Ireland 81 | Israel 82 | Italy 83 | Ivory Coast 84 | Jamaica 85 | Japan 86 | Jordan 87 | Kazakhstan 88 | Kenya 89 | Kiribati 90 | Korea North 91 | Korea South 92 | Kosovo 93 | Kuwait 94 | Kyrgyzstan 95 | Laos 96 | Latvia 97 | Lebanon 98 | Lesotho 99 | Liberia 100 | Libya 101 | Liechtenstein 102 | Lithuania 103 | Luxembourg 104 | Macedonia 105 | Madagascar 106 | Malawi 107 | Malaysia 108 | Maldives 109 | Mali 110 | Malta 111 | Marshall Islands 112 | Mauritania 113 | Mauritius 114 | Mexico 115 | Micronesia 116 | Moldova 117 | Monaco 118 | Mongolia 119 | Montenegro 120 | Morocco 121 | Mozambique 122 | Myanmar 123 | Namibia 124 | Nauru 125 | Nepal 126 | Netherlands 127 | New Zealand 128 | Nicaragua 129 | Niger 130 | Nigeria 131 | Norway 132 | Oman 133 | Pakistan 134 | Palau 135 | Panama 136 | Papua New Guinea 137 | Paraguay 138 | Peru 139 | Philippines 140 | Poland 141 | Portugal 142 | Qatar 143 | Romania 144 | Russian Federation 145 | Rwanda 146 | St Kitts & Nevis 147 | St Lucia 148 | Saint Vincent & the Grenadines 149 | Samoa 150 | San Marino 151 | Sao Tome & Principe 152 | Saudi Arabia 153 | Senegal 154 | Serbia 155 | Seychelles 156 | Sierra Leone 157 | Singapore 158 | Slovakia 159 | Slovenia 160 | Solomon Islands 161 | Somalia 162 | South Africa 163 | South Sudan 164 | Spain 165 | Sri Lanka 166 | Sudan 167 | Suriname 168 | Swaziland 169 | Sweden 170 | Switzerland 171 | Syria 172 | Taiwan 173 | Tajikistan 174 | Tanzania 175 | Thailand 176 | Togo 177 | Tonga 178 | Trinidad & Tobago 179 | Tunisia 180 | Turkey 181 | Turkmenistan 182 | Tuvalu 183 | Uganda 184 | Ukraine 185 | United Arab Emirates 186 | United Kingdom 187 | United States 188 | Uruguay 189 | Uzbekistan 190 | Vanuatu 191 | Vatican City 192 | Venezuela 193 | Vietnam 194 | Yemen 195 | Zambia 196 | Zimbabwe 197 | -------------------------------------------------------------------------------- /clickthebutton/data/dips.txt: -------------------------------------------------------------------------------- 1 | # https://en.wikipedia.org/wiki/Dipping_sauce#List_of_dips 2 | Ajika 3 | Ajvar 4 | Artichoke dip 5 | Baba ghanoush 6 | Bagna càuda 7 | Banana ketchup 8 | Barbecue sauce 9 | Bean dip 10 | Blue cheese dressing 11 | Buffalo sauce 12 | Cheese sauce 13 | Chile con queso 14 | Chili oil 15 | Chimichurri 16 | Chocolate 17 | Chogochujang 18 | Chutney 19 | Clam dip 20 | Cocktail sauce 21 | Comeback sauce 22 | Crab dip 23 | Curry ketchup 24 | Duck sauce 25 | Fish sauce 26 | Fish paste 27 | Fondue 28 | French onion dip 29 | Fritessaus 30 | Fry sauce 31 | Garlic butter sauce 32 | Gravy 33 | Guacamole 34 | Hazelnut butter 35 | Honey 36 | Honey mustard 37 | Horseradish sauce 38 | Hot sauce or chili sauce 39 | Hummus 40 | Jus 41 | Ketchup 42 | Kiwi onion dip 43 | Marinara sauce 44 | Mayonnaise 45 | Mexicali dip 46 | Mint sauce 47 | Mkhali 48 | Muhammara 49 | Mustard 50 | Nacho cheese dip 51 | Nam chim 52 | Nam phrik 53 | Nước chấm 54 | Pebre 55 | Pimento cheese 56 | Plum sauce 57 | Ranch dressing 58 | Remoulade 59 | Romesco 60 | Salsa 61 | Sambal 62 | Satsivi 63 | Smetana 64 | Sour cream 65 | Soy sauce 66 | Spinach dip 67 | Sriracha sauce 68 | Sweet and sour sauce 69 | Taramosalata 70 | Tartar sauce 71 | Tentsuyu 72 | Tirokafteri 73 | Tkemali 74 | Toyomansi 75 | Tzatziki 76 | Vinegar 77 | Vin Santo 78 | -------------------------------------------------------------------------------- /clickthebutton/data/fortnite_named_locations.txt: -------------------------------------------------------------------------------- 1 | # https://fortnite.fandom.com/wiki/Named_Locations 2 | Brutal Boxcars 3 | Burd 4 | Canyon Crossing 5 | Demon's Dojo 6 | Flooded Frogs 7 | Foxy Floodgate 8 | Hopeful Heights 9 | Lost Lake 10 | Magic Mosses 11 | Masked Meadows 12 | Nightshift Forest 13 | Pumped Power 14 | Seaport City 15 | Shining Span 16 | Shogun's Solitude 17 | Twinkle Terrace 18 | Warrior's Watch 19 | Whiffy Wharf 20 | Kappa Kappa Factory 21 | Shattered Span 22 | Classy Courts 23 | Fencing Fields 24 | Grand Glacier 25 | Hazy Hillside 26 | Lavish Lair 27 | Pleasant Piazza 28 | Rebel's Roost 29 | Reckless Railways 30 | Ritzy Riviera 31 | Ruined Reels 32 | Snooty Steppes 33 | Brawler's Battleground 34 | Mount Olympus 35 | Grim Gate 36 | Restored Reels 37 | The Underworld 38 | Brutal Beachhead 39 | Nitrodrome 40 | Redline Rig 41 | Sandy Steppes 42 | Shipwreck Shallows 43 | The Raft 44 | Castle Doom 45 | Doom's Courtyard 46 | Doomstadt 47 | Freaky Fields 48 | Brawler's Patch 49 | Anvil Square 50 | Breakwater Bay 51 | Brutal Bastion 52 | Faulty Splits 53 | Frenzy Fields 54 | Lonely Labs 55 | Shattered Slabs 56 | Slappy Shores 57 | The Citadel 58 | Kenjutsu Crossing 59 | Knotty Nets 60 | MEGA City 61 | Steamy Springs 62 | Creeky Compound 63 | Shady Stilts 64 | Rumble Ruins 65 | Eclipsed Estate 66 | Relentless Retreat 67 | Sanguine Suites 68 | Camp Cuddle 69 | Chonker's Speedway 70 | Coney Crossroads 71 | Condo Canyon 72 | Covert Cavern 73 | Greasy Grove 74 | Logjam Lumberyard 75 | Rocky Reels 76 | Sanctuary 77 | Shifty Shafts 78 | Sleepy Sound 79 | The Daily Bugle 80 | The Joneses 81 | Tilted Towers 82 | Command Cavern 83 | Synapse Station 84 | The Collider 85 | The Fortress 86 | Rave Cave 87 | Reality Falls 88 | Logjam Lotus 89 | Shuffled Shrines 90 | Lazy Lagoon 91 | Logjam Junction 92 | Cloudy Condos 93 | Fort Jonesy 94 | Herald's Sanctum 95 | Lustrous Lagoon 96 | Reality Tree 97 | Shimmering Shrine 98 | Chrome Crossroads 99 | Flutter Barn 100 | Grim Gables 101 | Chromejam Junction 102 | Shiny Sound 103 | Tainted Towers 104 | Craggy Cliffs 105 | Dirty Docks 106 | Frenzy Farm 107 | Holly Hedges 108 | Lazy Lake 109 | Misty Meadows 110 | Pleasant Park 111 | Retail Row 112 | Salty Springs 113 | Slurpy Swamp 114 | Steamy Stacks 115 | Sweaty Sands 116 | Weeping Woods 117 | The Agency 118 | The Grotto 119 | The Rig 120 | The Shark 121 | The Yacht 122 | Catty Corner 123 | Rickety Rig 124 | The Authority 125 | The Fortilla 126 | Coral Castle 127 | Doom's Domain 128 | Stark Industries 129 | The Ruins 130 | Salty Towers 131 | Colossal Coliseum 132 | Hunter's Haven 133 | Stealthy Stronghold 134 | Colossal Crops 135 | Believer Beach 136 | Boney Burbs 137 | The Spire 138 | Holly Hatchery 139 | Sludgy Swamp 140 | Corny Crops 141 | The Convergence 142 | The Pyramid 143 | The Doggpound 144 | Spaghetti Grotto 145 | Ice Isle 146 | PS 147 | Anarchy Acres 148 | Fatal Fields 149 | Flush Factory 150 | Greasy Grove 151 | Lonely Lodge 152 | Loot Lake 153 | Moisty Mire 154 | Pleasant Park 155 | Retail Row 156 | Wailing Woods 157 | Dusty Depot 158 | Salty Springs 159 | Tomato Town 160 | Haunted Hills 161 | Junk Junction 162 | Shifty Shafts 163 | Snobby Shores 164 | Tilted Towers 165 | Lucky Landing 166 | Dusty Divot 167 | Risky Reels 168 | Lazy Links 169 | Tomato Temple 170 | Paradise Palms 171 | Leaky Lake 172 | Frosty Flights 173 | Happy Hamlet 174 | The Block 175 | Lazy Lagoon 176 | Sunny Steps 177 | Mega Mall 178 | Neo Tilted 179 | Pressure Plant 180 | Tilted Town 181 | Moisty Palms 182 | Gotham City 183 | Starry Suburbs 184 | The Plaza 185 | Main Stage 186 | The Big Screen 187 | Dusty Docks 188 | Lazy Laps 189 | Lil'Loot Lake 190 | Lone Lodge 191 | Pleasant Park 192 | Retail Row 193 | Snobby Shoals 194 | Sandy Sheets 195 | Tilted Towers 196 | Adobe Abodes 197 | Fossil Fields 198 | Guaco Town 199 | Lizard Links 200 | Paradise Palms 201 | Shady Springs 202 | Snobby Sands 203 | Sunburnt Shafts 204 | Twisted Trailers 205 | -------------------------------------------------------------------------------- /clickthebutton/data/instruments.txt: -------------------------------------------------------------------------------- 1 | # https://simple.wikipedia.org/wiki/List_of_musical_instruments 2 | Afghani guitar 3 | Alghoza 4 | Accordion 5 | Bagpipes 6 | Banjo 7 | Banjo cello 8 | Bass banjo 9 | Five-stringed banjo 10 | Bluegrass banjo 11 | Four-stringed banjo 12 | Plectrum banjo 13 | Six-stringed banjo 14 | Tenor banjo 15 | Zither banjo 16 | Bass guitar 17 | Bassoon 18 | Contrabassoon/double bassoon 19 | Tenoroon 20 | Berimbau 21 | Bongo 22 | Chimta 23 | Calliope 24 | Cello 25 | Clarinet 26 | Alto clarinet 27 | Bass clarinet 28 | Basset clarinet 29 | Basset horn 30 | Clarinette d'amour 31 | Contra-alto clarinet 32 | Contrabass clarinet 33 | Piccolo clarinet 34 | A♭ clarinet 35 | Sopranino clarinet 36 | E♭ clarinet 37 | D clarinet 38 | Soprano clarinet 39 | C clarinet 40 | B♭ clarinet 41 | A clarinet 42 | G clarinet 43 | Octocontra-alto clarinet 44 | Octocontrabass clarinet 45 | Clavichord 46 | Cor anglais 47 | Cornet 48 | Cymbal 49 | Dhime 50 | Didgeridoo 51 | Dizi 52 | Double bass 53 | Drum kit 54 | Esraj 55 | Ektara 56 | Erhu 57 | Euphonium 58 | Fiddle 59 | Flute 60 | Alto flute 61 | Bass flute 62 | Contra-alto flute 63 | Contrabass flute 64 | Subcontrabass flute 65 | Double contrabass flute 66 | Hyperbass flute 67 | French horn 68 | Ghatam 69 | Box drum 70 | Glass harmonica 71 | Glockenspiel 72 | Gong 73 | Guitar 74 | Acoustic guitar 75 | Baritone guitar 76 | Bass guitar 77 | Chitarra battente 78 | Cigar box guitar 79 | Classical guitar 80 | Electric guitar 81 | Electric bass guitar 82 | Extended-range guitars 83 | Alto guitar 84 | Seven-string guitar 85 | Eight-string guitar 86 | Ten-string guitar 87 | Harp guitar 88 | Flamenco guitar 89 | Fusetar 90 | Guitarra quinta huapanguera 91 | Guitar synthesizer 92 | Guitarrón (argentino) 93 | Guitarrón (chileno) 94 | Guitarrón (mexicano) 95 | Guitarrón (uruguayo) 96 | Gut-stringed guitars 97 | Lap steel guitars 98 | Dobro 99 | National Steel 100 | Multi-necked guitars 101 | Double-neck guitar 102 | Triple-neck guitar 103 | Quadruple-neck guitar 104 | Five-neck guitar 105 | Six-neck guitar 106 | Seven-neck guitar 107 | Eight-neck guitar 108 | Rock Ock 109 | Twelve-neck guitar 110 | Nine-string guitar 111 | Nylon-string guitars 112 | Octave guitar 113 | Pedal steel guitar 114 | Resophonic guitar 115 | Steel-string acoustic guitar 116 | Tenor guitar 117 | Terz guitar 118 | Twelve-string guitar 119 | Yotar 120 | Guqin 121 | Guzheng 122 | Hang 123 | Harmonica 124 | Chromatic harmonica 125 | Diatonic harmonica 126 | Tremolo harmonica 127 | Orchestral harmonica 128 | ChenGong harmonica 129 | Harmonium 130 | Harp 131 | Harpsichord 132 | Hammered dulcimer 133 | Hulusi 134 | Hurdy gurdy 135 | Indian Banjo 136 | Jew’s harp 137 | Kalimba 138 | Lute 139 | Lyre 140 | Mandolin 141 | Mando-bass 142 | Mandocello 143 | Mandola 144 | Octave mandolin 145 | Marimba 146 | Melodica 147 | Oboe 148 | Bass/Baritone oboe 149 | Contrabass oboe 150 | Cor anglais/English horn 151 | Oboe d'amore 152 | Oboe da caccia 153 | Piccolo oboe/Oboe musette 154 | Ocarina 155 | Transverse ocarina 156 | Pendant ocarina 157 | Inline ocarina 158 | Multi chambered ocarina 159 | Keyed ocarina 160 | Slide ocarina 161 | Octobass 162 | Organ 163 | Otamatone 164 | Oud 165 | Pan Pipes 166 | Panduri 167 | Pennywhistle 168 | Piano 169 | Piccolo 170 | Pipa 171 | Pungi 172 | Rabab 173 | Recorder 174 | Garklein recorder 175 | Sopranino recorder 176 | Descant recorder 177 | Treble recorder 178 | Tenor recorder 179 | Bass recorder 180 | Great bass recorder 181 | Contra bass recorder 182 | Subcontra bass recorder 183 | Sub-subcontrabass recorder 184 | Venova 185 | Sarangi 186 | Sarinda 187 | Suroz 188 | Sursinger 189 | Santoor 190 | Sarod 191 | Saxophone 192 | Alto saxophone 193 | Baritone saxophone 194 | Bass saxophone 195 | C melody saxophone 196 | C Soprano saxophone 197 | Contrabass saxophone 198 | Mezzo-soprano saxophone (Alto in F) 199 | Sopranino saxophone 200 | Soprano saxophone 201 | Soprillo 202 | Subcontrabass saxophone 203 | Tenor saxophone 204 | Tubax 205 | Venova 206 | Xaphoon 207 | Shehnai 208 | Sheng 209 | Sitar 210 | Steelpan 211 | Stylophone 212 | Suona 213 | Synthesizer 214 | Tabla 215 | Tambourine 216 | Timpani 217 | Florguitar 218 | Triangle 219 | Trombone 220 | Sopranino trombone 221 | Soprano trombone 222 | Alto trombone 223 | Tenor trombone 224 | Bass trombone 225 | Contrabass trombone 226 | Valve trombone 227 | Renaissance trombone 228 | Trumpet 229 | Soprano trumpet 230 | Bass trumpet 231 | Baroque trumpet 232 | Bass trumpet 233 | Rotary valve trumpet 234 | Theremin 235 | Thundertube (Spring Drum) 236 | Tuba 237 | Bass tuba 238 | Contrabass tuba 239 | Subcontrabass tuba 240 | Ukulele 241 | Pocket ukulele 242 | Soprano ukulele 243 | Concert ukulele 244 | Tenor ukulele 245 | Eight-string tenor 246 | Five-string tenor 247 | Lili'u 248 | Six-string tenor 249 | Baritone ukulele 250 | Bass ukulele 251 | Contrabass ukulele 252 | U-bass 253 | Cigar box ukulele 254 | Veena 255 | Viola 256 | Violin 257 | Piccolo violino 258 | Viola 259 | Viola d'amore 260 | Tenor viola 261 | Tenor violin 262 | Baritone violin 263 | Electric violin 264 | Cello (violoncello) 265 | Piccolo cello 266 | Whamola 267 | Xylophone 268 | Zither 269 | Alpine zither (harp zither) 270 | Concert zither 271 | Guitar zither 272 | Overtone zither 273 | -------------------------------------------------------------------------------- /clickthebutton/data/kpop_artists.txt: -------------------------------------------------------------------------------- 1 | # https://en.wikipedia.org/wiki/List_of_K-pop_artists 2 | Ahn Jae-hyo 3 | Ajoo 4 | Alex Chu 5 | Alexander Lee 6 | Allen Kim 7 | Andrew Choi 8 | Andy Lee 9 | Aron 10 | B-Bomb 11 | B.I 12 | Babylon 13 | Bae Jin-young 14 | Bae Ki-sung 15 | Baekho 16 | Baekhyun 17 | BamBam 18 | Bang Ye-dam 19 | Bang Yong-guk 20 | Baro 21 | Bernard Park 22 | BM 23 | Bobby 24 | Boi B 25 | Boo Seung-kwan 26 | Brian Joo 27 | Bumkey 28 | Bumzu 29 | Byung Hun 30 | Cha Eun-woo 31 | Chae Bo-hun 32 | Chan 33 | Chancellor 34 | Changjo 35 | Changmin 36 | Chen 37 | Cho Kyu-hyun 38 | Cho Seung-youn 39 | Choi Bo-min 40 | Choi Byung-chan 41 | Choi Hyun-suk 42 | Choi Jong-hoon 43 | Choi Jung-won 44 | Choi Min-ho 45 | Choi Min-hwan 46 | Choi Si-won 47 | Choi Sung-min 48 | Choi Young-jae 49 | Choiza 50 | Chun Myung-hoon 51 | CNU 52 | Cream 53 | Crush 54 | D.O. 55 | Daesung 56 | Danny Ahn 57 | David Oh 58 | Dawn 59 | DJ Clazzi 60 | DJ Shine 61 | Do Han-se 62 | Doyoung 63 | Eddy Kim 64 | Eli Kim 65 | Eric Mun 66 | Eric Nam 67 | Eru 68 | Eun Ji-won 69 | Eunhyuk 70 | G-Dragon 71 | G.O 72 | G.Soul 73 | Gaeko 74 | Gaho 75 | Gary 76 | Gill 77 | Gong Myung 78 | Gongchan 79 | H-Eugene 80 | Ha Dong-kyun 81 | Ha Sebin 82 | Ha Sung-woon 83 | Han Geng 84 | Han Hee-jun 85 | Han Seung-woo 86 | Hangzoo 87 | Hanhae 88 | Henry Lau 89 | Heo Jung-min 90 | Heo Young-saeng 91 | Hongseok 92 | Hoya 93 | Huang Zitao 94 | Huh Gak 95 | Hui 96 | Hwang Chan-sung 97 | Hwang Chi-yeul 98 | Hwang Kwang-hee 99 | Hwang Min-hyun 100 | Hwanhee 101 | Hyuk 102 | Hyun Jin-young 103 | Hyungwon 104 | I'll 105 | I.M 106 | Im Hyun-sik 107 | Im Si-wan 108 | J-Hope 109 | Jackson Wang 110 | Jae Park 111 | Jaehyun 112 | Jang Beom-june 113 | Jang Dae-hyeon 114 | Jang Dong-woo 115 | Jang Han-byul 116 | Jang Hyun-seung 117 | Jang Jin-young 118 | Jang Minho 119 | Jang Su-won 120 | Jang Woo-hyuk 121 | Jang Wooyoung 122 | Jay 123 | Jay B 124 | Jay Park 125 | Jeong Jin-hwan 126 | Jeong Jinwoon 127 | Jeong Se-woon 128 | Jero 129 | Jimin 130 | Jin Longguo 131 | Jinho 132 | Jinjin 133 | Jinu 134 | Jinyoung 135 | Jo Kwang-min 136 | Jo Kwon 137 | Jo Young-min 138 | John Park 139 | Joo Jong-hyuk 140 | Joohoney 141 | Joon Park 142 | JR 143 | Jun Jin 144 | Jun. K 145 | Jun.Q 146 | Jung Chan-woo 147 | Jung Dae-hyun 148 | Jung Il-hoon 149 | Jung Jin-young 150 | Jung Joon-young 151 | Jung Seung-hwan 152 | Jung Yong-hwa 153 | Jung Yoon-hak 154 | Junggigo 155 | Jungkook 156 | Jungyup 157 | Justin 158 | K.Will 159 | Kai 160 | Kang Chan-hee 161 | Kang Daniel 162 | Kang Doo 163 | Kang In-soo 164 | Kang Kyun-sung 165 | Kang Min-hyuk 166 | Kang Seung-yoon 167 | Kang Sung-hoon 168 | Kang Tae-oh 169 | Kangin 170 | Kangnam 171 | Kangta 172 | Kanto 173 | Kasper 174 | Kebee 175 | Ken 176 | Kevin Moon 177 | Kevin Oh 178 | Kevin Woo 179 | Key 180 | Kidoh 181 | Kiggen 182 | Kihyun 183 | Kim C 184 | Kim Dong-han 185 | Kim Dong-hyun 186 | Kim Dong-hyun 187 | Kim Dong-jun 188 | Kim Dong-wan 189 | Kim Feel 190 | Kim Hee-chul 191 | Kim Hyun-joong 192 | Kim Hyung-jun 193 | Kim Jae-duck 194 | Kim Jae-hwan 195 | Kim Jae-hyun 196 | Kim Jae-joong 197 | Kim Jae-yong 198 | Kim Jeong-hoon 199 | Kim Ji-hoon 200 | Kim Ji-hoon 201 | Kim Ji-soo 202 | Kim Jin-ho 203 | Kim Jin-woo 204 | Kim Jong-hyun 205 | Kim Jong-kook 206 | Kim Jong-min 207 | Kim Joon 208 | Kim Jung-mo 209 | Kim Jung-woo 210 | Kim Junsu 211 | Kim Ki-bum 212 | Kim Kyu-jong 213 | Kim Min-kyu 214 | Kim Ryeo-wook 215 | Kim Se-yong 216 | Kim Seok-jin 217 | Kim Sun-woong 218 | Kim Sung-jae 219 | Kim Sung-joo 220 | Kim Sung-kyu 221 | Kim Tae-woo 222 | Kim Woo-jin 223 | Kim Woo-seok 224 | Kim Woo-sung 225 | Kim Yo-han 226 | Kim Yong-jun 227 | Kino 228 | Ko Ji-yong 229 | Koo Jun-hoe 230 | Kris Wu 231 | Kwak Jin-eon 232 | Kwon Hyun-bin 233 | L 234 | Lai Kuan-lin 235 | Lay Zhang 236 | Lee Chan-hyuk 237 | Lee Chang-min 238 | Lee Chang-sub 239 | Lee Dae-hwi 240 | Lee Donghae 241 | Lee Eun-sang 242 | Lee Gi-kwang 243 | Lee Gun-woo 244 | Lee Hong-bin 245 | Lee Hong-gi 246 | Lee Hyun 247 | Lee Jae-hoon 248 | Lee Jae-jin 249 | Lee Jae-won 250 | Lee Jae-yoon 251 | Lee Jai-jin 252 | Lee Je-no 253 | Lee Jin-hyuk 254 | Lee Jong-hwa 255 | Lee Jong-hyun 256 | Lee Joon 257 | Lee Jun-ho 258 | Lee Jun-young 259 | Lee Jung 260 | Lee Jung-shin 261 | Lee Ki-seop 262 | Lee Min-hyuk 263 | Lee Min-hyuk 264 | Lee Min-woo 265 | Lee Sang-min 266 | Lee Seok-hoon 267 | Lee Seung-gi 268 | Lee Seung-hoon 269 | Lee Seung-hyub 270 | Lee Sung-jong 271 | Lee Sung-min 272 | Lee Sung-yeol 273 | Lee Tae-hwan 274 | Lee Tae-il 275 | Lee Tae-min 276 | Lee Tae-yong 277 | Lee You-jin 278 | Leeteuk 279 | Leo 280 | Li Wenhan 281 | Lil Boi 282 | Lim Seul-ong 283 | Louie 284 | Lu Han 285 | Lucas Wong 286 | Mark Lee 287 | Mark Tuan 288 | Mikey 289 | Min Kyung-hoon 290 | Mino 291 | Minos 292 | Mir 293 | Mithra Jin 294 | MJ 295 | Moon Bin 296 | Moon Hee-joon 297 | Moon Ji-hoo 298 | Moon Jong-up 299 | Moon Joon-young 300 | Moon Tae-il 301 | N 302 | Na Jae-min 303 | Nam Tae-hyun 304 | Nam Woo-hyun 305 | Naul 306 | Nichkhun 307 | Niel 308 | No Min-woo 309 | Oh Jong-hyuk 310 | Oh Se-hun 311 | Ok Taec-yeon 312 | One 313 | Onew 314 | Ong Seong-wu 315 | P.O 316 | Parc Jae-jung 317 | Park Chanyeol 318 | Park Geon-il 319 | Park Hyung-sik 320 | Park Ji-hoon 321 | Park Jin-young 322 | Park Jung-min 323 | Park Kyung 324 | Park Si-hwan 325 | Park Sun-ho 326 | Park Sung-hoon 327 | Park Woo-jin 328 | Park Yong-ha 329 | Park Yoo-chun 330 | Peniel Shin 331 | Psy 332 | Qin Fen 333 | Rain 334 | Ravi 335 | Ren 336 | RM 337 | Rocky 338 | Roh Ji-hoon 339 | Roh Tae-hyun 340 | Rowoon 341 | Roy Kim 342 | Sam Kim 343 | Samuel 344 | San E 345 | Sanchez 346 | Sandeul 347 | Seo Eun-kwang 348 | Seo In-guk 349 | Seo Kang-joon 350 | Seo Min-woo 351 | Seo Taiji 352 | Seungri 353 | Seven 354 | Shin Dong-ho 355 | Shin Hye-sung 356 | Shin Jong-kook 357 | Shin Jung-hwan 358 | Shin Seung-hun 359 | Shin Won-ho 360 | Shindong 361 | Shownu 362 | Sleepy 363 | Son Dong-woon 364 | Son Ho-jun 365 | Son Ho-young 366 | Song Seung-hyun 367 | Song Yuvin 368 | Suga 369 | Suho 370 | Sung Hoon 371 | Swings 372 | T.O.P 373 | Tablo 374 | Taebin 375 | Taeyang 376 | Takuya Terada 377 | Teddy Park 378 | Ten 379 | The8 380 | Thunder 381 | Tiger JK 382 | Tony Ahn 383 | U-Kwon 384 | V 385 | Wang Yibo 386 | Wen Junhui 387 | Wonho 388 | Woody 389 | Wooseok 390 | Woozi 391 | Wuno 392 | Xiumin 393 | Yang Hyun-suk 394 | Yang Seung-ho 395 | Yang Yo-seob 396 | Yeo Hoon-min 397 | Yeo One 398 | Yesung 399 | Yong Jun-hyung 400 | Yoo Il 401 | Yoo Jae-suk 402 | Yoo Seon-ho 403 | Yoo Seung-jun 404 | Yoo Seung-woo 405 | Yoo Young-jae 406 | Yook Sung-jae 407 | Yoon Do-hyun 408 | Yoon Doo-joon 409 | Yoon Hyun-sang 410 | Yoon Ji-sung 411 | Yoon Jong-shin 412 | Yoon Kye-sang 413 | Yoon Min-soo 414 | Yoon San-ha 415 | Young K 416 | Youngjun 417 | Yugyeom 418 | Yunho 419 | Yuta Nakamoto 420 | Yuto Adachi 421 | Zelo 422 | Zhong Chenle 423 | Zhou Mi 424 | Zhou Yixuan 425 | Zhu Zhengting 426 | Zick Jasper 427 | Zico 428 | Ah Young 429 | Ahn Ji-young 430 | Ahn So-hee 431 | Ahn Sol-bin 432 | Ailee 433 | AleXa 434 | Alexandra Reid 435 | Ali 436 | Amber Liu 437 | An Ye-seul 438 | An Yu-jin 439 | Anda 440 | Arin 441 | Bada 442 | Bae Seul-ki 443 | Bae Suzy 444 | Bae Woo-hee 445 | Baek A-yeon 446 | Baek Ji-young 447 | Baek Ye-rin 448 | Bang Min-ah 449 | Ben 450 | BoA 451 | Bona 452 | Byul 453 | Cao Lu 454 | Chae Jung-an 455 | Chae Ri-na 456 | Chaeyoung 457 | Cheng Xiao 458 | Cheris Lee 459 | Cho Mi-yeon 460 | Cho Seung-hee 461 | Choi Han-bit 462 | Choi Jung-in 463 | Choi Soo-eun 464 | Choi Soo-young 465 | Choi Ye-na 466 | Choi Yoo-jung 467 | Choi Yu-jin 468 | Chu Ye-jin 469 | Chungha 470 | Chuu 471 | CL 472 | Dahyun 473 | Dana 474 | Dawon 475 | Dia 476 | Elly 477 | Elkie Chong 478 | Esna 479 | Eugene 480 | Euna Kim 481 | Eunha 482 | Eunwoo 483 | Exy 484 | Eyedi 485 | Fat Cat 486 | Fei 487 | Ferlyn Wong 488 | G.NA 489 | Gain 490 | Gil Hak-mi 491 | Gilme 492 | Go Woo-ri 493 | Goo Hara 494 | Gummy 495 | Hahm Eun-jung 496 | Han Chae-young 497 | Han Hye-ri 498 | Han Seung-yeon 499 | Han Sun-hwa 500 | Hana 501 | Hani 502 | Harisu 503 | Heo Ga-yoon 504 | Heo Sol-ji 505 | Heo Young-ji 506 | Hitomi Honda 507 | Hong Jin-kyung 508 | Hong Jin-young 509 | Horan 510 | Huh Chan-mi 511 | Hwangbo 512 | Hwang In-sun 513 | Hwang Jung-eum 514 | Hwasa 515 | Hwayobi 516 | Hynn 517 | Hyojung 518 | Hyolyn 519 | Hyomin 520 | Hyuna 521 | I 522 | Im Yoon-ah 523 | Irene 524 | IU 525 | Ivy 526 | Jamie 527 | Jang Jae-in 528 | Jang Na-ra 529 | Jang Won-young 530 | Jang Ye-eun 531 | JeA 532 | Jennie 533 | Jeon Boram 534 | Jeon Hye-bin 535 | Jeon Ji-yoon 536 | Jeon So-yeon 537 | Jeon Somi 538 | Jeong Eun-ji 539 | Jeongyeon 540 | Jessi 541 | Jessica Jung 542 | Jia 543 | Jihyo 544 | Jisoo 545 | Jiyul 546 | Jo Eun-byul 547 | Jo Yu-ri 548 | Johyun 549 | Joo 550 | JooE 551 | Joy 552 | Jun Hyo-seong 553 | Jung Chae-yeon 554 | Jung Da-eun 555 | Jung Ha-na 556 | Jung Ryeo-won 557 | Juniel 558 | Juri Takahashi 559 | Jurina Matsui 560 | Kahi 561 | Kan Mi-youn 562 | Kang Ji-young 563 | Kang Mi-na 564 | Kang Min-hee 565 | Kang Min-kyung 566 | Kang Se-jung 567 | Kang Si-ra 568 | Kang Ye-seo 569 | Kang Ye-won 570 | Katie Kim 571 | Kei 572 | Ki Hui-hyeon 573 | Kim Ah-joong 574 | Kim Chanmi 575 | Kim Da-som 576 | Kim Do-ah 577 | Kim Do-yeon 578 | Kim E-Z 579 | Kim Ga-young 580 | Kim Greem 581 | Kim Hyo-yeon 582 | Kim Isak 583 | Kim Jae-kyung 584 | Kim Ji-hyun 585 | Kim Ju-na 586 | Kim Jung-ah 587 | Kim Min-ju 588 | Kim Min-seo 589 | Kim Nam-joo 590 | Kim Se-jeong 591 | Kim Seol-hyun 592 | Kim Si-hyeon 593 | Kim So-hee 594 | Kim So-hee 595 | Kim So-hye 596 | Kim So-jung 597 | Kim Sook 598 | Kim Wan-sun 599 | Kim Ye-won 600 | Kim Yeon-ji 601 | Kim Yoon-ah 602 | Kim Yoon-ji 603 | Kim Yu-bin 604 | Kriesha Chu 605 | Krystal Jung 606 | Kwon Eun-bi 607 | Kwon Eun-bin 608 | Kwon Jin-ah 609 | Kwon Mina 610 | Kwon Nara 611 | Kwon Ri-se 612 | Kwon So-hyun 613 | Kwon Yu-ri 614 | Lee Ahyumi 615 | Lee Bo-ram 616 | Lee Chae-yeon 617 | Lee Hae-in 618 | Lee Hae-in 619 | Lee Hae-na 620 | Lee Hae-ri 621 | Lee Hee-jin 622 | Lee Hi 623 | Lee Hwa-kyum 624 | Lee Hye-ri 625 | Lee Hyori 626 | Lee Hyun-joo 627 | Lee Ji-hye 628 | Lee Ji-hyun 629 | Lee Jin 630 | Lee Jin-ah 631 | Lee Joo-yeon 632 | Lee Ju-eun 633 | Lee Jung-hyun 634 | Lee Na-eun 635 | Lee So-jung 636 | Lee Soo-jung 637 | Lee Soo-mi 638 | Lee Soo-min 639 | Lee Soo-young 640 | Lee Su-hyun 641 | Lee Su-ji 642 | Lee Sun-bin 643 | Lee Young-yoo 644 | Lee Yu-ri 645 | Lena Park 646 | Lexie Liu 647 | Lexy 648 | Lim Jeong-hee 649 | Lim Kim 650 | Lim Na-young 651 | Lina 652 | Lisa 653 | Liu Xiening 654 | Luda 655 | Luna 656 | Lyn 657 | Mako Kojima 658 | Meng Meiqi 659 | Michelle Lee 660 | Miho Miyazaki 661 | Mijoo 662 | Min 663 | Min Do-hee 664 | Min Hae-kyung 665 | Min Hyo-rin 666 | Mina 667 | Minnie 668 | Mint 669 | Minzy 670 | Miru Shiroma 671 | Miryo 672 | Miyu Takeuchi 673 | Momo Hirai 674 | Moonbyul 675 | Na Hae-ryung 676 | Nada 677 | Nako Yabuki 678 | Nam Gyu-ri 679 | Nana 680 | Nancy 681 | Narsha 682 | Natty 683 | Nayeon 684 | NC.A 685 | Nicole Jung 686 | Ock Joo-hyun 687 | Oh Ha-young 688 | Oh Seung-ah 689 | Oh Yeon-seo 690 | Park Bo-ram 691 | Park Bom 692 | Park Cho-a 693 | Park Cho-rong 694 | Park Gyeong-ree 695 | Park Gyu-ri 696 | Park Hee-von 697 | Park Hye-su 698 | Park Jeong-hwa 699 | Park Ji-yeon 700 | Park Ji-yoon 701 | Park Jung-ah 702 | Park Si-eun 703 | Park So-jin 704 | Park So-yeon 705 | Park Soo-ah 706 | Park Soo-jin 707 | Park Subin 708 | Park Ye-eun 709 | Park Yoon-ha 710 | Pearl 711 | Qri 712 | Ra Mi-ran 713 | Raina 714 | Reina Kubo 715 | Rena Hasegawa 716 | Rosé 717 | Rothy 718 | Rumble Fish 719 | Ryu Hwa-young 720 | Ryu Hyo-young 721 | Ryu Se-ra 722 | Ryu Su-jeong 723 | Sae Murase 724 | Sakura Miyawaki 725 | Sana 726 | Sandara Park 727 | Seo Hye-lin 728 | Seo Hyun-jin 729 | Seo In-young 730 | Seo Ji-young 731 | Seo Yu-na 732 | Seohyun 733 | Seola 734 | Seulgi 735 | Seunghee 736 | Shannon 737 | Shim Eun-jin 738 | Shim Mina 739 | Shin Hye-jeong 740 | Shin Ji 741 | Shin Ji-hoon 742 | Shin Ji-min 743 | Shoo 744 | SinB 745 | Solar 746 | Solbi 747 | Somin 748 | Son Dam-bi 749 | Son Ji-hyun 750 | Son Na-eun 751 | Son Seung-yeon 752 | Song Ji-eun 753 | Song Yuqi 754 | Soojin 755 | Sori 756 | Sorn 757 | Soy Kim 758 | Soya 759 | Soyou 760 | Stella Jang 761 | Stephanie 762 | Sulli 763 | Sunday 764 | Sung Yu-ri 765 | Sunmi 766 | Sunny 767 | Sunye 768 | Taeyeon 769 | Tasha Low 770 | Tiffany Young 771 | Tomu Muto 772 | Tzuyu 773 | U Sung-eun 774 | U;Nee 775 | Uee 776 | Uhm Jung-hwa 777 | Umji 778 | Victoria Song 779 | Viki 780 | Wax 781 | Wendy 782 | Wheein 783 | Woo Hye-mi 784 | Woo Hye-rim 785 | Wu Xuanyi 786 | Xiyeon 787 | Xu Ziyin 788 | Yang Hye-sun 789 | Yang Yoo-jin 790 | Yebin 791 | Yeonwoo 792 | Yeri 793 | Yerin 794 | Yezi 795 | Yoo Ara 796 | Yoo Chae-yeong 797 | Yoo So-young 798 | Yoo Yeon-jung 799 | YooA 800 | Yoon Bo-mi 801 | Yoon Bo-ra 802 | Yoon Chae-kyung 803 | Yoon Eun-hye 804 | Yoon Mi-rae 805 | Yua Mikami 806 | Yuju 807 | Yūka Kato 808 | Yukika Teramoto 809 | Yura 810 | Yuri 811 | Z.Hera 812 | Zhang Bichen 813 | Zhang Liyin 814 | Zhou Jieqiong 815 | -------------------------------------------------------------------------------- /clickthebutton/data/languages.txt: -------------------------------------------------------------------------------- 1 | # https://github.com/umpirsky/language-list/tree/master/data/en_CA 2 | Abkhazian 3 | Achinese 4 | Acoli 5 | Adangme 6 | Adyghe 7 | Afar 8 | Afrihili 9 | Afrikaans 10 | Aghem 11 | Ainu 12 | Akan 13 | Akkadian 14 | Akoose 15 | Alabama 16 | Albanian 17 | Aleut 18 | Algerian Arabic 19 | American English 20 | Amharic 21 | Ancient Egyptian 22 | Ancient Greek 23 | Angika 24 | Ao Naga 25 | Arabic 26 | Aragonese 27 | Aramaic 28 | Araona 29 | Arapaho 30 | Arawak 31 | Armenian 32 | Aromanian 33 | Arpitan 34 | Assamese 35 | Asturian 36 | Asu 37 | Atsam 38 | Australian English 39 | Austrian German 40 | Avaric 41 | Avestan 42 | Awadhi 43 | Aymara 44 | Azerbaijani 45 | Badaga 46 | Bafia 47 | Bafut 48 | Bakhtiari 49 | Balinese 50 | Baluchi 51 | Bambara 52 | Bamun 53 | Banjar 54 | Basaa 55 | Bashkir 56 | Basque 57 | Batak Toba 58 | Bavarian 59 | Beja 60 | Belarusian 61 | Bemba 62 | Bena 63 | Bengali 64 | Betawi 65 | Bhojpuri 66 | Bikol 67 | Bini 68 | Bishnupriya 69 | Bislama 70 | Blin 71 | Blissymbols 72 | Bodo 73 | Bosnian 74 | Brahui 75 | Braj 76 | Brazilian Portuguese 77 | Breton 78 | British English 79 | Buginese 80 | Bulgarian 81 | Bulu 82 | Buriat 83 | Burmese 84 | Caddo 85 | Cajun French 86 | Canadian English 87 | Canadian French 88 | Cantonese 89 | Capiznon 90 | Carib 91 | Catalan 92 | Cayuga 93 | Cebuano 94 | Central Atlas Tamazight 95 | Central Dusun 96 | Central Kurdish 97 | Central Yupik 98 | Chadian Arabic 99 | Chagatai 100 | Chamorro 101 | Chechen 102 | Cherokee 103 | Cheyenne 104 | Chibcha 105 | Chiga 106 | Chimborazo Highland Quichua 107 | Chinese 108 | Chinook Jargon 109 | Chipewyan 110 | Choctaw 111 | Church Slavic 112 | Chuukese 113 | Chuvash 114 | Classical Newari 115 | Classical Syriac 116 | Colognian 117 | Comorian 118 | Congo Swahili 119 | Coptic 120 | Cornish 121 | Corsican 122 | Cree 123 | Creek 124 | Crimean Turkish 125 | Croatian 126 | Czech 127 | Dakota 128 | Danish 129 | Dargwa 130 | Dazaga 131 | Delaware 132 | Dinka 133 | Divehi 134 | Dogri 135 | Dogrib 136 | Duala 137 | Dutch 138 | Dyula 139 | Dzongkha 140 | Eastern Frisian 141 | Efik 142 | Egyptian Arabic 143 | Ekajuk 144 | Elamite 145 | Embu 146 | Emilian 147 | English 148 | Erzya 149 | Esperanto 150 | Estonian 151 | European Portuguese 152 | European Spanish 153 | Ewe 154 | Ewondo 155 | Extremaduran 156 | Fang 157 | Fanti 158 | Faroese 159 | Fiji Hindi 160 | Fijian 161 | Filipino 162 | Finnish 163 | Flemish 164 | Fon 165 | Frafra 166 | French 167 | Friulian 168 | Fulah 169 | Ga 170 | Gagauz 171 | Galician 172 | Gan Chinese 173 | Ganda 174 | Gayo 175 | Gbaya 176 | Geez 177 | Georgian 178 | German 179 | Gheg Albanian 180 | Ghomala 181 | Gilaki 182 | Gilbertese 183 | Goan Konkani 184 | Gondi 185 | Gorontalo 186 | Gothic 187 | Grebo 188 | Greek 189 | Guarani 190 | Gujarati 191 | Gusii 192 | Gwichʼin 193 | Haida 194 | Haitian 195 | Hakka Chinese 196 | Hausa 197 | Hawaiian 198 | Hebrew 199 | Herero 200 | Hiligaynon 201 | Hindi 202 | Hiri Motu 203 | Hittite 204 | Hmong 205 | Hungarian 206 | Hupa 207 | Iban 208 | Ibibio 209 | Icelandic 210 | Ido 211 | Igbo 212 | Iloko 213 | Inari Sami 214 | Indonesian 215 | Ingrian 216 | Ingush 217 | Interlingua 218 | Interlingue 219 | Inuktitut 220 | Inupiaq 221 | Irish 222 | Italian 223 | Jamaican Creole English 224 | Japanese 225 | Javanese 226 | Jju 227 | Jola-Fonyi 228 | Judeo-Arabic 229 | Judeo-Persian 230 | Jutish 231 | Kabardian 232 | Kabuverdianu 233 | Kabyle 234 | Kachin 235 | Kaingang 236 | Kako 237 | Kalaallisut 238 | Kalenjin 239 | Kalmyk 240 | Kamba 241 | Kanembu 242 | Kannada 243 | Kanuri 244 | Kara-Kalpak 245 | Karachay-Balkar 246 | Karelian 247 | Kashmiri 248 | Kashubian 249 | Kawi 250 | Kazakh 251 | Kenyang 252 | Khasi 253 | Khmer 254 | Khotanese 255 | Khowar 256 | Kikuyu 257 | Kimbundu 258 | Kinaray-a 259 | Kinyarwanda 260 | Kirmanjki 261 | Klingon 262 | Kom 263 | Komi 264 | Komi-Permyak 265 | Kongo 266 | Konkani 267 | Korean 268 | Koro 269 | Kosraean 270 | Kotava 271 | Koyra Chiini 272 | Koyraboro Senni 273 | Kpelle 274 | Krio 275 | Kuanyama 276 | Kumyk 277 | Kurdish 278 | Kurukh 279 | Kutenai 280 | Kwasio 281 | Kyrgyz 282 | Kʼicheʼ 283 | Ladino 284 | Lahnda 285 | Lakota 286 | Lamba 287 | Langi 288 | Lao 289 | Latgalian 290 | Latin 291 | Latin American Spanish 292 | Latvian 293 | Laz 294 | Lezghian 295 | Ligurian 296 | Limburgish 297 | Lingala 298 | Lingua Franca Nova 299 | Literary Chinese 300 | Lithuanian 301 | Livonian 302 | Lojban 303 | Lombard 304 | Low German 305 | Lower Silesian 306 | Lower Sorbian 307 | Lozi 308 | Luba-Katanga 309 | Luba-Lulua 310 | Luiseno 311 | Lule Sami 312 | Lunda 313 | Luo 314 | Luxembourgish 315 | Luyia 316 | Maba 317 | Macedonian 318 | Machame 319 | Madurese 320 | Mafa 321 | Magahi 322 | Main-Franconian 323 | Maithili 324 | Makasar 325 | Makhuwa-Meetto 326 | Makonde 327 | Malagasy 328 | Malay 329 | Malayalam 330 | Maltese 331 | Manchu 332 | Mandar 333 | Mandingo 334 | Manipuri 335 | Manx 336 | Maori 337 | Mapuche 338 | Marathi 339 | Mari 340 | Marshallese 341 | Marwari 342 | Masai 343 | Mazanderani 344 | Medumba 345 | Mende 346 | Mentawai 347 | Meru 348 | Metaʼ 349 | Mexican Spanish 350 | Micmac 351 | Middle Dutch 352 | Middle English 353 | Middle French 354 | Middle High German 355 | Middle Irish 356 | Min Nan Chinese 357 | Minangkabau 358 | Mingrelian 359 | Mirandese 360 | Mizo 361 | Modern Standard Arabic 362 | Mohawk 363 | Moksha 364 | Moldavian 365 | Mongo 366 | Mongolian 367 | Morisyen 368 | Moroccan Arabic 369 | Mossi 370 | Mundang 371 | Muslim Tat 372 | Myene 373 | Nama 374 | Nauru 375 | Navajo 376 | Ndonga 377 | Neapolitan 378 | Nepali 379 | Newari 380 | Ngambay 381 | Ngiemboon 382 | Ngomba 383 | Nheengatu 384 | Nias 385 | Niuean 386 | Nogai 387 | North Ndebele 388 | Northern Frisian 389 | Northern Sami 390 | Northern Sotho 391 | Norwegian 392 | Norwegian Bokmål 393 | Norwegian Nynorsk 394 | Novial 395 | Nuer 396 | Nyamwezi 397 | Nyanja 398 | Nyankole 399 | Nyasa Tonga 400 | Nyoro 401 | Nzima 402 | NʼKo 403 | Occitan 404 | Ojibwa 405 | Old English 406 | Old French 407 | Old High German 408 | Old Irish 409 | Old Norse 410 | Old Persian 411 | Old Provençal 412 | Oriya 413 | Oromo 414 | Osage 415 | Ossetic 416 | Ottoman Turkish 417 | Pahlavi 418 | Palatine German 419 | Palauan 420 | Pali 421 | Pampanga 422 | Pangasinan 423 | Papiamento 424 | Pashto 425 | Pennsylvania German 426 | Persian 427 | Phoenician 428 | Picard 429 | Piedmontese 430 | Plautdietsch 431 | Pohnpeian 432 | Polish 433 | Pontic 434 | Portuguese 435 | Prussian 436 | Punjabi 437 | Quechua 438 | Rajasthani 439 | Rapanui 440 | Rarotongan 441 | Riffian 442 | Romagnol 443 | Romanian 444 | Romansh 445 | Romany 446 | Rombo 447 | Root 448 | Rotuman 449 | Roviana 450 | Rundi 451 | Russian 452 | Rusyn 453 | Rwa 454 | Saho 455 | Sakha 456 | Samaritan Aramaic 457 | Samburu 458 | Samoan 459 | Samogitian 460 | Sandawe 461 | Sango 462 | Sangu 463 | Sanskrit 464 | Santali 465 | Sardinian 466 | Sasak 467 | Sassarese Sardinian 468 | Saterland Frisian 469 | Saurashtra 470 | Scots 471 | Scottish Gaelic 472 | Selayar 473 | Selkup 474 | Sena 475 | Seneca 476 | Serbian 477 | Serbo-Croatian 478 | Serer 479 | Seri 480 | Shambala 481 | Shan 482 | Shona 483 | Sichuan Yi 484 | Sicilian 485 | Sidamo 486 | Siksika 487 | Silesian 488 | Simplified Chinese 489 | Sindhi 490 | Sinhala 491 | Skolt Sami 492 | Slave 493 | Slovak 494 | Slovenian 495 | Soga 496 | Sogdien 497 | Somali 498 | Soninke 499 | South Azerbaijani 500 | South Ndebele 501 | Southern Altai 502 | Southern Sami 503 | Southern Sotho 504 | Spanish 505 | Sranan Tongo 506 | Standard Moroccan Tamazight 507 | Sukuma 508 | Sumerian 509 | Sundanese 510 | Susu 511 | Swahili 512 | Swati 513 | Swedish 514 | Swiss French 515 | Swiss German 516 | Swiss High German 517 | Syriac 518 | Tachelhit 519 | Tagalog 520 | Tahitian 521 | Taita 522 | Tajik 523 | Talysh 524 | Tamashek 525 | Tamil 526 | Taroko 527 | Tasawaq 528 | Tatar 529 | Telugu 530 | Tereno 531 | Teso 532 | Tetum 533 | Thai 534 | Tibetan 535 | Tigre 536 | Tigrinya 537 | Timne 538 | Tiv 539 | Tlingit 540 | Tok Pisin 541 | Tokelau 542 | Tongan 543 | Tornedalen Finnish 544 | Traditional Chinese 545 | Tsakhur 546 | Tsakonian 547 | Tsimshian 548 | Tsonga 549 | Tswana 550 | Tulu 551 | Tumbuka 552 | Tunisian Arabic 553 | Turkish 554 | Turkmen 555 | Turoyo 556 | Tuvalu 557 | Tuvinian 558 | Twi 559 | Tyap 560 | Udmurt 561 | Ugaritic 562 | Ukrainian 563 | Umbundu 564 | Upper Sorbian 565 | Urdu 566 | Uyghur 567 | Uzbek 568 | Vai 569 | Venda 570 | Venetian 571 | Veps 572 | Vietnamese 573 | Volapük 574 | Võro 575 | Votic 576 | Vunjo 577 | Walloon 578 | Walser 579 | Waray 580 | Warlpiri 581 | Washo 582 | Wayuu 583 | Welsh 584 | West Flemish 585 | Western Frisian 586 | Western Mari 587 | Wolaytta 588 | Wolof 589 | Wu Chinese 590 | Xhosa 591 | Xiang Chinese 592 | Yangben 593 | Yao 594 | Yapese 595 | Yemba 596 | Yiddish 597 | Yoruba 598 | Zapotec 599 | Zarma 600 | Zaza 601 | Zeelandic 602 | Zenaga 603 | Zhuang 604 | Zoroastrian Dari 605 | Zulu 606 | Zuni 607 | -------------------------------------------------------------------------------- /clickthebutton/data/phobias.txt: -------------------------------------------------------------------------------- 1 | # https://en.wiktionary.org/w/api.php?action=query&cmlimit=500&cmprop=title&cmtitle=Category%3AEnglish_terms_suffixed_with_-phobia&list=categorymembers&format=json 2 | abibliophobia 3 | ablutophobia 4 | acarophobia 5 | acephobia 6 | achluophobia 7 | achondroplasiaphobia 8 | acousticophobia 9 | acrophobia 10 | addictophobia 11 | adverbophobia 12 | aerophobia 13 | Afrophobia 14 | agliophobia 15 | agoraphobia 16 | agraphobia 17 | aibohphobia 18 | aichmophobia 19 | AIDSphobia 20 | ailurophobia 21 | Albanophobia 22 | albuminurophobia 23 | alektorophobia 24 | alethophobia 25 | algiophobia 26 | altophobia 27 | amathophobia 28 | amaxophobia 29 | ambulophobia 30 | Americanophobia 31 | Americophobia 32 | amoebophobia 33 | anatidaephobia 34 | ancraophobia 35 | androphobia 36 | anemophobia 37 | Anglophobia 38 | anthophobia 39 | anthropophobia 40 | antlophobia 41 | apeirophobia 42 | aphenphosmphobia 43 | aphobia 44 | apiphobia 45 | aporophobia 46 | apotemnophobia 47 | aquaphobia 48 | Arabophobia 49 | arachibutyrophobia 50 | arachnophobia 51 | araneophobia 52 | arithmophobia 53 | Armenophobia 54 | arophobia 55 | arsonphobia 56 | Asiaphobia 57 | astraphobia 58 | astrapophobia 59 | astrophobia 60 | ataxophobia 61 | atelophobia 62 | atheophobia 63 | atomophobia 64 | atomphobia 65 | atychiphobia 66 | audiophobia 67 | aulophobia 68 | aurophobia 69 | auroraphobia 70 | Australophobia 71 | automatonophobia 72 | autophobia 73 | aviophobia 74 | bacillophobia 75 | bacteriophobia 76 | Balkanophobia 77 | bananaphobia 78 | barophobia 79 | basophobia 80 | bathmophobia 81 | bathophobia 82 | batophobia 83 | batrachophobia 84 | belonephobia 85 | bibliophobia 86 | biophobia 87 | biphobia 88 | Blackophobia 89 | blennophobia 90 | bozophobia 91 | brontophobia 92 | butchphobia 93 | cacophobia 94 | cainophobia 95 | caligynephobia 96 | Canadaphobia 97 | cancerphobia 98 | caniphobia 99 | cannaphobia 100 | carbophobia 101 | carcinomatophobia 102 | carcinophobia 103 | cardiophobia 104 | carnophobia 105 | Catalanophobia 106 | catoptrophobia 107 | Celtophobia 108 | centrophobia 109 | chaetophobia 110 | chelonaphobia 111 | chemophobia 112 | cherophobia 113 | Chinaphobia 114 | chionophobia 115 | chlorophobia 116 | chorophobia 117 | chrematophobia 118 | Christianophobia 119 | chrometophobia 120 | chromophobia 121 | chronomentrophobia 122 | chrysophobia 123 | cibophobia 124 | cinephobia 125 | cisphobia 126 | claustrophobia 127 | cleithrophobia 128 | climacophobia 129 | clinophobia 130 | clithrophobia 131 | clownophobia 132 | coimetrophobia 133 | coitophobia 134 | colorphobia 135 | colourphobia 136 | commitmentphobia 137 | Communistphobia 138 | computerphobia 139 | conservaphobia 140 | coprophobia 141 | coronaphobia 142 | corticophobia 143 | cosmophobia 144 | coulrophobia 145 | cremnophobia 146 | cryophobia 147 | cyanophobia 148 | cyberphobia 149 | Cymrophobia 150 | cynophobia 151 | cypridophobia 152 | decidophobia 153 | defecalgesiophobia 154 | deipnophobia 155 | demonophobia 156 | demophobia 157 | dendrophobia 158 | dentophobia 159 | dermatopathophobia 160 | dextrophobia 161 | didaskaleinophobia 162 | dikastophobia 163 | dipsophobia 164 | Disneyphobia 165 | disposophobia 166 | dodecaphobia 167 | domatophobia 168 | domophobia 169 | doraphobia 170 | doxophobia 171 | dragphobia 172 | Dubyaphobia 173 | Dutchphobia 174 | dykephobia 175 | dysmorphophobia 176 | dystychiphobia 177 | ecclesiophobia 178 | econophobia 179 | ecophobia 180 | eisoptrophobia 181 | elasmophobia 182 | electrophobia 183 | eleutherophobia 184 | emetophobia 185 | enbyphobia 186 | entomophobia 187 | eosophobia 188 | ephebiphobia 189 | epistolophobia 190 | equinophobia 191 | eremophobia 192 | ergasiophobia 193 | ergophobia 194 | erotophobia 195 | erythrophobia 196 | ethnophobia 197 | euphobia 198 | Europhobia 199 | eurotophobia 200 | fatphobia 201 | felinophobia 202 | femmephobia 203 | Francophobia 204 | friggatriskaidekaphobia 205 | frigophobia 206 | fructophobia 207 | galeophobia 208 | Gallophobia 209 | gametophobia 210 | gamophobia 211 | gatophobia 212 | gayphobia 213 | geliophobia 214 | gelotophobia 215 | genderphobia 216 | genophobia 217 | Georgiaphobia 218 | Georgiophobia 219 | gephyrophobia 220 | gerascophobia 221 | Germanophobia 222 | germophobia 223 | gerontophobia 224 | gingerphobia 225 | globophobia 226 | gnomophobia 227 | gonophobia 228 | grammatophobia 229 | graphophobia 230 | gringophobia 231 | gymnophobia 232 | gynaecophobia 233 | gynophobia 234 | haematophobia 235 | haemophobia 236 | hagiophobia 237 | halitophobia 238 | handiphobia 239 | haphephobia 240 | hebephobia 241 | hedonophobia 242 | heliophobia 243 | Hellenophobia 244 | helminthophobia 245 | hemophobia 246 | heresyphobia 247 | herpetophobia 248 | heterophobia 249 | hexakosioihexekontahexaphobia 250 | Hibernophobia 251 | hierophobia 252 | hijabophobia 253 | Hinduphobia 254 | hippophobia 255 | hippopotomonstrosesquipedaliophobia 256 | Hispanophobia 257 | Hitlerphobia 258 | hodophobia 259 | homichlophobia 260 | homoerotophobia 261 | homonymophobia 262 | homonymphobia 263 | homophobia 264 | homophobiaphobia 265 | homophobophobia 266 | hoplophobia 267 | Hungarophobia 268 | hydrophobia 269 | hylophobia 270 | hypnophobia 271 | hypsophobia 272 | iatrophobia 273 | ichthyophobia 274 | iconophobia 275 | ideophobia 276 | incestophobia 277 | Indophobia 278 | infantophobia 279 | insectophobia 280 | Internetphobia 281 | interphobia 282 | intersexphobia 283 | Iranophobia 284 | Irishphobia 285 | Islamophobia 286 | Israelophobia 287 | Italophobia 288 | ithyphallophobia 289 | Japanophobia 290 | Judaeophobia 291 | Judeophobia 292 | juvenophobia 293 | kainotophobia 294 | kakorrhaphiophobia 295 | kathisophobia 296 | kenophobia 297 | keraunophobia 298 | kleptophobia 299 | koinophobia 300 | koumpounophobia 301 | Kurdophobia 302 | lachanophobia 303 | lalophobia 304 | Latinophobia 305 | lepidopterophobia 306 | leporiphobia 307 | lepraphobia 308 | leprophobia 309 | lesbophobia 310 | leukophobia 311 | LGBTphobia 312 | liberalphobia 313 | lilapsophobia 314 | lipophobia 315 | logizomechanophobia 316 | logophobe 317 | logophobia 318 | Lusophobia 319 | lutraphobia 320 | lycophobia 321 | lygophobia 322 | lyssophobia 323 | mageiricophobia 324 | maieusiophobia 325 | Malayophobia 326 | maniaphobia 327 | mathophobia 328 | mathphobia 329 | matrophobia 330 | mechanophobia 331 | medicophobia 332 | megalophobia 333 | melanoheliophobia 334 | melanophobia 335 | melissophobia 336 | melophobia 337 | menophobia 338 | metallophobia 339 | metrophobia 340 | Mexiphobia 341 | microbiophobia 342 | microphobia 343 | molluscophobia 344 | monologophobia 345 | monophobia 346 | moralphobia 347 | Mormonophobia 348 | motorphobia 349 | mottephobia 350 | musicophobia 351 | Muslimaphobia 352 | Muslimophobia 353 | Muslimphobia 354 | musophobia 355 | mycophobia 356 | myrmecophobia 357 | mysophobia 358 | Naziphobia 359 | necrophobia 360 | Negrophobia 361 | neopharmaphobia 362 | neophobia 363 | nephophobia 364 | noctiphobia 365 | noctophobia 366 | nomophobia 367 | normalphobia 368 | nosocomephobia 369 | nosophobia 370 | nostophobia 371 | novercaphobia 372 | nucleomitophobia 373 | numerophobia 374 | nyctohylophobia 375 | nyctophobia 376 | obesophobia 377 | ochlophobia 378 | octophobia 379 | odontophobia 380 | oenophobia 381 | oikophobia 382 | ombrophobia 383 | omniphobia 384 | omphalophobia 385 | oneirophobia 386 | onomatophobia 387 | ophidiophobia 388 | ophiophobia 389 | ophthalmophobia 390 | opiophobia 391 | optophobia 392 | ornithophobia 393 | osmophobia 394 | ouranophobia 395 | Pakistanphobia 396 | panophobia 397 | panphobia 398 | pantophobia 399 | papaphobia 400 | papyrophobia 401 | parasitophobia 402 | parthenophobia 403 | pathophobia 404 | pediculophobia 405 | pediophobia 406 | pedophobia 407 | peladophobia 408 | peniaphobia 409 | phagophobia 410 | phallophobia 411 | pharmacophobia 412 | phasmophobia 413 | phengophobia 414 | philemaphobia 415 | philophobia 416 | phobiaphobia 417 | phobophobia 418 | phonophobia 419 | photophobia 420 | photophonophobia 421 | phthisiophobia 422 | pistanthrophobia 423 | pluralphobia 424 | plutophobia 425 | pogonophobia 426 | politicophobia 427 | Polonophobia 428 | polyphobia 429 | pomophobia 430 | ponophobia 431 | porcophobia 432 | pornophobia 433 | porphyrophobia 434 | posthephobia 435 | potamophobia 436 | pozphobia 437 | praeputiophobia 438 | pronounphobia 439 | psychophobia 440 | psychrophobia 441 | pteronophobia 442 | pupaphobia 443 | pyrophobia 444 | queerphobia 445 | radiophobia 446 | ranidaphobia 447 | rectophobia 448 | reeferphobia 449 | rhabdophobia 450 | rhypophobia 451 | rhytiphobia 452 | robophobia 453 | rodentophobia 454 | Romanophobia 455 | Romaphobia 456 | rupophobia 457 | Russiaphobia 458 | Russophobia 459 | samhainophobia 460 | Santaphobia 461 | sarmassophobia 462 | Satanophobia 463 | scabiophobia 464 | schoolphobia 465 | sciencephobia 466 | scopophobia 467 | scotomaphobia 468 | scotophobia 469 | Scotophobia 470 | Scottophobia 471 | seismophobia 472 | selachophobia 473 | selenophobia 474 | Serbophobia 475 | serophobia 476 | sesquipedalophobia 477 | sexophobia 478 | sexphobia 479 | Shariaphobia 480 | Shiaphobia 481 | siderodromophobia 482 | Sinophobia 483 | sissyphobia 484 | sitophobia 485 | Slavophobia 486 | snakephobia 487 | sociophobia 488 | Somaliphobia 489 | Somalophobia 490 | somatophobia 491 | somniphobia 492 | sonophobia 493 | sophophobia 494 | spectrophobia 495 | speluncaphobia 496 | spermatophobia 497 | spheksophobia 498 | spiderphobia 499 | stenophobia 500 | steroidphobia 501 | submechanophobia 502 | substratophobia 503 | Sunniphobia 504 | symbolophobia 505 | symmetrophobia 506 | syphilophobia 507 | taphophobia 508 | taurophobia 509 | technophobia 510 | teleophobia 511 | teratophobia 512 | tetraphobia 513 | Teutonophobia 514 | Teutophobia 515 | thaasophobia 516 | thanatophobia 517 | theatrophobia 518 | theophobia 519 | thermophobia 520 | tomophobia 521 | topophobia 522 | toxicophobia 523 | transphobia 524 | traumatophobia 525 | trichophobia 526 | triskaidecaphobia 527 | triskaidekaphobia 528 | tropophobia 529 | Trumphobia 530 | Trumpophobia 531 | truthaphobia 532 | truthophobia 533 | trypanophobia 534 | trypophobia 535 | tyrannophobia 536 | Ukrainophobia 537 | uranophobia 538 | uxorophobia 539 | vaccinophobia 540 | vaginaphobia 541 | vegaphobia 542 | vegephobia 543 | venereophobia 544 | venustraphobia 545 | verbophobia 546 | videophobia 547 | whitephobia 548 | whorephobia 549 | Wiccaphobia 550 | wikiphobia 551 | workphobia 552 | xanthophobia 553 | xenoglossophobia 554 | xenophobia 555 | xerophobia 556 | xylophobia 557 | zeusophobia 558 | zoophobia 559 | zoöphobia 560 | -------------------------------------------------------------------------------- /clickthebutton/data/skeld_locations.txt: -------------------------------------------------------------------------------- 1 | # https://among-us.fandom.com/wiki/Locations#The_Skeld 2 | Admin 3 | Cafeteria 4 | Communications 5 | Electrical 6 | Lower Engine 7 | MedBay 8 | Navigation 9 | O2 10 | Reactor 11 | Security 12 | Shields 13 | Storage 14 | Upper Engine 15 | Weapons 16 | -------------------------------------------------------------------------------- /clickthebutton/data/star_rail_characters.txt: -------------------------------------------------------------------------------- 1 | # https://www.prydwen.gg/page-data/star-rail/characters/page-data.json 2 | Acheron 3 | Aglaea 4 | Anaxa 5 | Argenti 6 | Arlan 7 | Asta 8 | Aventurine 9 | Bailu 10 | Black Swan 11 | Blade 12 | Boothill 13 | Bronya 14 | Castorice 15 | Clara 16 | Dan Heng 17 | Dan Heng • Imbibitor Lunae 18 | Dr. Ratio 19 | Feixiao 20 | Firefly 21 | Fu Xuan 22 | Gallagher 23 | Gepard 24 | Guinaifen 25 | Hanya 26 | Herta 27 | Himeko 28 | Hook 29 | Huohuo 30 | Jade 31 | Jiaoqiu 32 | Jing Yuan 33 | Jingliu 34 | Kafka 35 | Lingsha 36 | Luka 37 | Luocha 38 | Lynx 39 | March 7th 40 | March 7th • The Hunt 41 | Misha 42 | Moze 43 | Mydei 44 | Natasha 45 | Pela 46 | Qingque 47 | Rappa 48 | Robin 49 | Ruan Mei 50 | Sampo 51 | Seele 52 | Serval 53 | Silver Wolf 54 | Sparkle 55 | Sunday 56 | Sushang 57 | The Herta 58 | Tingyun 59 | Tingyun • Fugue 60 | Topaz & Numby 61 | Trailblazer • Destruction 62 | Trailblazer • Harmony 63 | Trailblazer • Preservation 64 | Trailblazer • Remembrance 65 | Tribbie 66 | Welt 67 | Xueyi 68 | Yanqing 69 | Yukong 70 | Yunli 71 | -------------------------------------------------------------------------------- /clickthebutton/data/tft_comps.txt: -------------------------------------------------------------------------------- 1 | # https://tftacademy.com/api/tierlist/comps 2 | [Dominator Silco]() 3 | [Operation: Find Jinx]() 4 | [Emissary Flex - Corki]() 5 | [Rising Rebellion]() 6 | [Ohana Means Family]() 7 | [Chem-Baron Cashout]() 8 | [20/20 Vision]() 9 | [Command & Conquer]() 10 | [Daddy Daughter Menace]() 11 | [Pit Fighter Flex]() 12 | [Black Sorcery]() 13 | [Quickstrikers - Nocturne Akali]() 14 | [Silver Scraps]() 15 | [Academy]() 16 | [Why Not Both?]() 17 | [Artillessaries]() 18 | [Martial Law / Betrayal]() 19 | [Combat Medic - Enforcers]() 20 | [Mad Chemist]() 21 | [R U Trolling?]() 22 | [IRELIAING]() 23 | [Crimson Pact]() 24 | [Famille Reunion]() 25 | [Gloves Off]() 26 | [Geniuses Heimer Ekko]() 27 | [Brutal Revenge]() 28 | [Experiment Twitch Flex]() 29 | [Au-Kogmaw-ta]() 30 | [Emissary Sorcs]() 31 | [Visionary Heimerdinger]() 32 | [Ambusher Reroll]() 33 | [Bruiser Twitch]() 34 | [The Black Rat]() 35 | [Sentinel Tristana]() 36 | [Dominators Ziggs]() 37 | [Built Different]() 38 | [Emissary Flex - Form Swapper]() 39 | [Bruiser Tristana]() 40 | [3 Cost Conqueror Reroll]() 41 | [Fast 9 AP Legendaries]() 42 | [Artillerist Urgot]() 43 | [Zoom Zoom Zeri]() 44 | [Sorcerer Darius]() 45 | [Zentinels]() 46 | [Quickstrikers - TF / Loris]() 47 | [Combat Medic - Bruisers]() 48 | [Chem Baron Ambushers]() 49 | [Visionary Morgana]() 50 | [Chem Baron Visionaries]() 51 | [Scar'Maw]() 52 | [Twitch Snipers]() 53 | [Dominator Cassio Blitz]() 54 | [Experiment Sorceror]() 55 | [Hurricane Ezreal]() 56 | -------------------------------------------------------------------------------- /clickthebutton/data/time_complexities.txt: -------------------------------------------------------------------------------- 1 | # https://en.wikipedia.org/wiki/Time_complexity 2 | Constant 3 | Logarithmic 4 | Polylogarithmic 5 | Sub-linear 6 | Linear 7 | Quasilinear 8 | Sub-quadratic 9 | Polynomial 10 | Superpolynomial 11 | Quasi-polynomial 12 | Sub-exponential 13 | Exponential 14 | Factorial 15 | Double exponential 16 | -------------------------------------------------------------------------------- /clickthebutton/data/tone_indicators.txt: -------------------------------------------------------------------------------- 1 | # https://en.wikipedia.org/wiki/Tone_indicator 2 | /j 3 | /hj 4 | /js 5 | /s 6 | /sarc 7 | /srs 8 | /nsrs 9 | /lh 10 | /hlh 11 | /g 12 | /gen 13 | /i 14 | /ui 15 | /vu 16 | /ij 17 | /ref 18 | /t 19 | /nm 20 | /lu 21 | /nf 22 | /nbh 23 | /nsb 24 | /nay 25 | /ay 26 | /nbr 27 | /nv 28 | /nav 29 | /ot 30 | /th 31 | /cb 32 | /f 33 | /q 34 | /l 35 | /ly 36 | /lyr 37 | /c 38 | /m 39 | /li 40 | /rt 41 | /rh 42 | /hyp 43 | /e 44 | /ex 45 | /p 46 | /pl 47 | /r 48 | /a 49 | /sx 50 | /x 51 | /nsx 52 | /ns 53 | /pc 54 | /pos 55 | /nc 56 | /neg 57 | /neu 58 | /nh 59 | /npa 60 | /st 61 | /mj 62 | /ma 63 | /hsrs 64 | /hs 65 | /dr 66 | /dkm 67 | /cwh 68 | /cr 69 | /cur 70 | /mhly 71 | /msrs 72 | /ms 73 | /non 74 | /genq 75 | /gq 76 | /jbt 77 | -------------------------------------------------------------------------------- /clickthebutton/data/twice_songs.txt: -------------------------------------------------------------------------------- 1 | # https://api.spotify.com/v1 2 | 1 To 10 3 | 1, 3, 2 (JEONGYEON, MINA, TZUYU) 4 | 21:29 5 | 21:29 - Japanese ver. 6 | 24/7 7 | AFTER MOON 8 | Alcohol-Free 9 | Alcohol-Free (English ver.) 10 | Alcohol-Free - Japanese ver. 11 | BDZ 12 | BDZ (Korean Ver.) 13 | BEHIND THE MASK 14 | BELIEVER 15 | BETTER 16 | BETTER - Instrumental 17 | BLAME IT ON ME 18 | BLOOM 19 | BRAND NEW GIRL 20 | BRAND NEW GIRL - Instrumental 21 | BREAKTHROUGH (Korean Ver.) 22 | BRING IT BACK 23 | Baby Blue Love 24 | Basics 25 | Be OK 26 | Be as ONE 27 | Be as ONE (Korean Ver.) 28 | Beyond the Horizon 29 | Bitter Sweet 30 | Brave 31 | Breakthrough 32 | Breakthrough - Instrumental 33 | Breakthrough - taalthechoi Remix 34 | CACTUS 35 | CANDY 36 | CHEER UP 37 | CHEER UP - Japanese ver. 38 | CHILLAX 39 | CRAZY STUPID LOVE 40 | CRUEL 41 | CRY FOR ME 42 | CRY FOR ME (English ver.) 43 | CRY FOR ME - Japanese ver. 44 | Candy Boy 45 | Candy Pop 46 | Candy Pop - Instrumental 47 | Catch a Wave 48 | Catch a Wave - Instrumental 49 | Celebrate 50 | Changing! 51 | Conversation 52 | DEJAVU 53 | DEPEND ON YOU 54 | DIVE 55 | DO WHAT WE LIKE 56 | DON’T CALL ME AGAIN 57 | Dance Again 58 | Dance The Night Away 59 | Dance The Night Away - Japanese ver. 60 | Ding Dong 61 | Do It Again 62 | Don't Give Up 63 | Doughnut 64 | Doughnut (Instrumental) 65 | ESPRESSO 66 | EYE EYE EYES 67 | Echoes of heart 68 | F.I.L.A (Fall In Love Again) 69 | FANCY 70 | FANCY - Japanese ver. 71 | FIREWORK 72 | Fake & True 73 | Fanfare 74 | Fanfare - Instrumental 75 | Fanfare - Lee Hae Sol Remix 76 | Feel Special 77 | Feel Special - Japanese ver. 78 | Ffw 79 | First Time 80 | Flow like waves 81 | Four-leaf Clover 82 | GET LOUD 83 | GIRLS LIKE US 84 | GO HARD 85 | GOT THE THRILLS 86 | Going Crazy 87 | Gone 88 | Good at Love 89 | HANDLE IT 90 | HAPPY HAPPY 91 | HAPPY HAPPY - Instrumental 92 | HAPPY HAPPY - collapsedone Remix 93 | HELL IN HEAVEN 94 | HELLO (NAYEON, MOMO, CHAEYOUNG) 95 | HO! 96 | HOLD ME TIGHT 97 | HOT 98 | Hare Hare 99 | Hare Hare - Instrumental 100 | Heart Shaker 101 | Heart Shaker (GALACTIKA * Holiday Remix) 102 | Heart Shaker - Japanese ver. 103 | Here I am 104 | How u doin' 105 | I CAN'T STOP ME 106 | I CAN'T STOP ME (English Version) 107 | I CAN'T STOP ME - Japanese ver. 108 | I CAN’T STOP ME (feat. BOYS LIKE GIRLS) 109 | I GOT YOU 110 | I GOT YOU (Feat. Lauv) 111 | I GOT YOU (Feat. Lauv) (Instrumental) 112 | I GOT YOU (Garage ver.) 113 | I GOT YOU (Garage ver.) (Instrumental) 114 | I GOT YOU (Hyper ver.) 115 | I GOT YOU (Hyper ver.) (Instrumental) 116 | I GOT YOU (Instrumental) 117 | I GOT YOU (Lo-fi ver.) 118 | I GOT YOU (Lo-fi ver.) (Instrumental) 119 | I GOT YOU (Original) 120 | I GOT YOU (Original) (Instrumental) 121 | I GOT YOU (Sped Up ver.) 122 | I GOT YOU (Sped Up ver.) (Instrumental) 123 | I WANT YOU BACK 124 | I love you more than anyone 125 | I love you more than anyone - Instrumental 126 | ICON 127 | Ice Cream 128 | In the summer 129 | Inside of me 130 | Jaljayo Good Night 131 | Jelly Jelly 132 | Just be yourself 133 | KNOCK KNOCK - Japanese ver. 134 | Keeper 135 | Kiss My Troubles Away 136 | Knock Knock 137 | Kura Kura 138 | Kura Kura - Instrumental 139 | L.O.V.E 140 | LALALA 141 | LAST WALTZ 142 | LIKEY - Japanese ver. 143 | LOVE FOOLISH 144 | LOVE WARNING 145 | LUV ME 146 | LUV ME - Instrumental 147 | Like A Fool 148 | Like It Like It 149 | Like OOH-AHH - Japanese ver. 150 | Like Ooh-Ahh 151 | Likey 152 | Look At Me 153 | Love Line 154 | MAKE ME GO 155 | MOONLIGHT 156 | MOONLIGHT SUNRISE 157 | MOONLIGHT SUNRISE (Club remix) 158 | MOONLIGHT SUNRISE (House remix) 159 | MOONLIGHT SUNRISE (Instrumental) 160 | MOONLIGHT SUNRISE (Jonas Blue Remix) 161 | MOONLIGHT SUNRISE (R&B remix) 162 | MORE & MORE 163 | MORE & MORE (English Version) 164 | MORE & MORE (LEE HAE SOL Sped Up Remix) 165 | MORE & MORE - Japanese ver. 166 | Magical 167 | Merry & Happy 168 | Missing U 169 | My Headphones on 170 | NEW NEW 171 | Next Page 172 | ONE SPARK 173 | ONE SPARK (English ver.) 174 | ONE SPARK (House ver.) 175 | ONE SPARK (Instrumental) 176 | ONE SPARK (R&B ver.) 177 | ONE SPARK (Rave ver.) 178 | ONE SPARK (Tropical ver.) 179 | ONLY YOU 180 | OXYGEN 181 | Ocean Deep 182 | One In A Million 183 | One More Time 184 | One More Time - Instrumental 185 | PIECES OF LOVE 186 | POLISH 187 | PROMISE 188 | PUSH & PULL (JIHYO, SANA, DAHYUN) 189 | Peach Soda 190 | Perfect World 191 | Pink Lemonade 192 | Pink Lemonade - Instrumental 193 | Pit-a-Pat 194 | Ponytail 195 | Precious Love 196 | QUEEN 197 | Queen of Hearts 198 | RAINBOW 199 | REAL YOU 200 | REWIND 201 | RUSH 202 | Rollin' 203 | SAY SOMETHING 204 | SAY YES 205 | SAY YOU LOVE ME 206 | SCIENTIST 207 | SCIENTIST (R3HAB Remix) 208 | SCIENTIST - Japanese ver. 209 | SET ME FREE 210 | SET ME FREE (ARMNHMR Remix) (ENG) 211 | SET ME FREE (Carneyval Remix) 212 | SET ME FREE (ENG) 213 | SET ME FREE (Lindgren Remix) 214 | SET ME FREE (Lindgren Remix) (ENG) 215 | SET ME FREE (Tommy “TBHits” Brown Remix) 216 | SET ME FREE (Tommy “TBHits” Brown Remix) (ENG) 217 | SHADOW 218 | SHOT CLOCK 219 | SIGNAL 220 | SIGNAL - Japanese ver. 221 | SOMEONE LIKE ME 222 | SOS 223 | STAY BY MY SIDE 224 | STRAWBERRY 225 | STUCK 226 | STUCK IN MY HEAD 227 | STUCK IN MY HEAD - Japanese ver. 228 | SUNSET 229 | SWEET SUMMER DAY 230 | SWEET TALKER 231 | SWING 232 | Sandcastle 233 | Say it again 234 | Scandal 235 | Scorpion 236 | Scorpion - Instrumental 237 | Shot thru the heart 238 | Strategy 239 | Strategy (House) 240 | Strategy (Instrumental) 241 | Strategy (Moombahton) 242 | Strategy (Slom Remix) 243 | Strategy (feat. Megan Thee Stallion) 244 | Strategy (version 1.0) 245 | Strawberry Moon 246 | Strawberry Moon - Instrumental 247 | Stronger 248 | Sweetest Obsession 249 | THREE TIMES A DAY 250 | TICK TOCK 251 | TRICK IT 252 | TT 253 | TT - Japanese ver. 254 | TURN IT UP 255 | Talk that Talk 256 | Thank you, Family 257 | That's all I'm saying 258 | The Best Thing I Ever Did 259 | The Best Thing I Ever Did - Japanese ver. 260 | The Feels 261 | The Feels (Benny Benassi Remix Extended) 262 | The Feels (Benny Benassi Remix – Instrumental) 263 | The Feels (Benny Benassi Remix) 264 | The Feels (Ian Asher Remix) 265 | The Feels (Instrumental) 266 | The Feels (The Stereotypes Remix – Instrumental) 267 | The Feels (The Stereotypes Remix) 268 | The Feels (YVES V Remix – Instrumental) 269 | The Feels (YVES V Remix) 270 | The Reason Why 271 | The wish 272 | Touchdown 273 | Trouble 274 | Truth 275 | Tuk Tok 276 | Turtle 277 | UP NO MORE 278 | Voices of Delight 279 | WALLFLOWER 280 | Wake Me Up 281 | Wake Me Up - Instrumental 282 | What You Waiting For 283 | What is Love? 284 | What is Love? - Japanese ver. 285 | When We Were Kids 286 | Wishing 287 | Wonderful Day 288 | Wonderful Day (Instrumental) 289 | Woohoo 290 | Wow 291 | YES or YES 292 | YES or YES - Japanese ver. 293 | YOU GET ME 294 | YOUNG & WILD 295 | You In My Heart 296 | -------------------------------------------------------------------------------- /clickthebutton/data/valorant_abilities.txt: -------------------------------------------------------------------------------- 1 | # https://valorant-api.com/v1/agents?isPlayableCharacter=true 2 | Gekko's Wingman 3 | Gekko's Dizzy 4 | Gekko's Mosh Pit 5 | Gekko's Thrash 6 | Fade's Seize 7 | Fade's Haunt 8 | Fade's Prowler 9 | Fade's Nightfall 10 | Breach's Flashpoint 11 | Breach's Fault Line 12 | Breach's Aftershock 13 | Breach's Rolling Thunder 14 | Deadlock's Sonic Sensor 15 | Deadlock's Barrier Mesh 16 | Deadlock's GravNet 17 | Deadlock's Annihilation 18 | Tejo's Guided Salvo 19 | Tejo's Special Delivery 20 | Tejo's Armageddon 21 | Tejo's Stealth Drone 22 | Raze's Blast Pack 23 | Raze's Paint Shells 24 | Raze's Boom Bot 25 | Raze's Showstopper 26 | Chamber's Rendezvous 27 | Chamber's Trademark 28 | Chamber's Headhunter 29 | Chamber's Tour De Force 30 | KAY/O's FRAG/ment 31 | KAY/O's FLASH/drive 32 | KAY/O's ZERO/point 33 | KAY/O's NULL/cmd 34 | Skye's Trailblazer 35 | Skye's Guiding Light 36 | Skye's Regrowth 37 | Skye's Seekers 38 | Cypher's Cyber Cage 39 | Cypher's Spycam 40 | Cypher's Trapwire 41 | Cypher's Neural Theft 42 | Sova's Shock Bolt 43 | Sova's Recon Bolt 44 | Sova's Owl Drone 45 | Sova's Hunter's Fury 46 | Killjoy's Nanoswarm 47 | Killjoy's ALARMBOT 48 | Killjoy's TURRET 49 | Killjoy's Lockdown 50 | Harbor's Cove 51 | Harbor's Cascade 52 | Harbor's High Tide 53 | Harbor's Reckoning 54 | Vyse's Shear 55 | Vyse's Arc Rose 56 | Vyse's Razorvine 57 | Vyse's Steel Garden 58 | Viper's Poison Cloud 59 | Viper's Toxic Screen 60 | Viper's Snake Bite 61 | Viper's Viper's Pit 62 | Phoenix's Blaze 63 | Phoenix's Hot Hands 64 | Phoenix's Curveball 65 | Phoenix's Run it Back 66 | Astra's Nova Pulse 67 | Astra's Nebula / Dissipate 68 | Astra's Gravity Well 69 | Astra's Astral Form / Cosmic Divide 70 | Brimstone's Stim Beacon 71 | Brimstone's Incendiary 72 | Brimstone's Sky Smoke 73 | Brimstone's Orbital Strike 74 | Iso's Undercut 75 | Iso's Kill Contract 76 | Iso's Double Tap 77 | Iso's Contingency 78 | Clove's Pick-me-up 79 | Clove's Ruse 80 | Clove's Not Dead Yet 81 | Clove's Meddle 82 | Neon's High Gear 83 | Neon's Relay Bolt 84 | Neon's Fast Lane 85 | Neon's Overdrive 86 | Yoru's FAKEOUT 87 | Yoru's BLINDSIDE 88 | Yoru's GATECRASH 89 | Yoru's DIMENSIONAL DRIFT 90 | Sage's Slow Orb 91 | Sage's Healing Orb 92 | Sage's Barrier Orb 93 | Sage's Resurrection 94 | Reyna's Devour 95 | Reyna's Dismiss 96 | Reyna's Leer 97 | Reyna's Empress 98 | Omen's Paranoia 99 | Omen's Dark Cover 100 | Omen's Shrouded Step 101 | Omen's From the Shadows 102 | Jett's Updraft 103 | Jett's Tailwind 104 | Jett's Cloudburst 105 | Jett's Blade Storm 106 | -------------------------------------------------------------------------------- /clickthebutton/data/valorant_ultimates.txt: -------------------------------------------------------------------------------- 1 | # https://valorant-api.com/v1/agents?isPlayableCharacter=true 2 | Thrash 3 | Nightfall 4 | Rolling Thunder 5 | Annihilation 6 | Armageddon 7 | Showstopper 8 | Tour De Force 9 | NULL/cmd 10 | Seekers 11 | Neural Theft 12 | Hunter's Fury 13 | Lockdown 14 | Reckoning 15 | Steel Garden 16 | Viper's Pit 17 | Run it Back 18 | Cosmic Divide 19 | Orbital Strike 20 | Kill Contract 21 | Not Dead Yet 22 | Overdrive 23 | DIMENSIONAL DRIFT 24 | Resurrection 25 | Empress 26 | From the Shadows 27 | Blade Storm 28 | -------------------------------------------------------------------------------- /clickthebutton/data/wikipedia_guidelines.txt: -------------------------------------------------------------------------------- 1 | # https://en.wikipedia.org/wiki/Wikipedia:Shortcut_index?action=raw 2 | WP:AIV 3 | WP:AN 4 | WP:AN3 5 | WP:AN/3RR 6 | WP:ANI 7 | WP:AN/I 8 | WP:ARBREQ 9 | WP:RFAR 10 | WP:AC 11 | WP:AC/C 12 | WP:AFC 13 | WP:AFD 14 | WP:LFD 15 | WP:AFD/T 16 | WP:AFD/TODAY 17 | WP:AFD/Y 18 | WP:AFDM 19 | WP:AFDO 20 | WP:AFDP 21 | WP:BL 22 | WP:BACK 23 | WP:BACKLOG 24 | WP:CFD 25 | WP:CFDC 26 | WP:CLEANUP 27 | WP:CTOPS 28 | WP:AC/CT 29 | WP:CP 30 | WP:CV 31 | WP:DRV 32 | WP:VFU 33 | WP:IFD 34 | WP:IFC 35 | WP:LTA 36 | WP:WM 37 | WP:MC 38 | WP:MFD 39 | WP:PR 40 | WP:PM 41 | WP:RFD 42 | WP:RA 43 | WP:RM 44 | WP:RP 45 | WP:RFA 46 | WP:VFA 47 | WP:RFC 48 | WP:RFROAA 49 | WP:RFC/USER 50 | WP:SPLICE 51 | WP:RFM 52 | WP:RFO 53 | WP:RFPP 54 | WP:RFP 55 | WP:RPP 56 | WP:RFASUM 57 | WP:TFD 58 | WP:TRL 59 | WP:ADMIN 60 | WP:SYSOP 61 | WP:MOP 62 | WP:AAB 63 | WP:APPEAL 64 | WP:AT 65 | WP:TITLE 66 | WP:BAN 67 | WP:LIVING 68 | WP:BLP 69 | WP:BP 70 | WP:BLOCK 71 | WP:BOTPOL 72 | WP:CON 73 | WP:CONS 74 | WP:CSD 75 | WP:CIVIL 76 | WP:C 77 | WP:COPY 78 | WP:DP 79 | WP:DEL 80 | WP:DELETE 81 | WP:DR 82 | WP:EW 83 | WP:WAR 84 | WP:EP 85 | WP:HA 86 | WP:IAR 87 | WP:IUP 88 | WP:EXEMPT 89 | WP:IPEXEMPT 90 | WP:IPBE 91 | WP:LOP 92 | WP:NPOV 93 | WP:NEU 94 | WP:NLT 95 | WP:LEGAL 96 | WP:THREAT 97 | WP:NOR 98 | WP:OR 99 | WP:NPA 100 | WP:PA 101 | WP:OWN 102 | WP:PAG 103 | WP:POLICY 104 | WP:RULES 105 | WP:PROD 106 | WP:SOCK 107 | WP:NIU 108 | WP:NOU 109 | WP:U 110 | WP:UN 111 | WP:VAND 112 | WP:VANDAL 113 | WP:-( 114 | WP:V 115 | WP:VERIFY 116 | WP:NOT 117 | WP:WWIN 118 | WP:WWPIN 119 | WP:ISNOT 120 | WP:ALSONOT 121 | WP:ENC 122 | WP:WINAD 123 | WP:DICDEF 124 | WP:LGL 125 | WP:LOGL 126 | WP:GUIDELINES 127 | WP:AGF 128 | WP:ASG 129 | WP:AFG 130 | WP:GF 131 | WP:FAITH 132 | WP:BB 133 | WP:BBIUP 134 | WP:BBIEP 135 | WP:BOLD 136 | WP:SOFIXIT 137 | WP:CLS 138 | WP:CSL 139 | WP:CG 140 | WP:CAT 141 | WP:CITE 142 | WP:REF 143 | WP:CK 144 | WP:COMKNOW 145 | WP:COI 146 | WP:DGFA 147 | WP:D 148 | WP:DAB 149 | WP:DISAM 150 | WP:DISAMBIG 151 | WP:DE 152 | WP:DISRUPT 153 | WP:POINT 154 | WP:EL 155 | WP:EXT 156 | WP:LOGOS 157 | WP:LOGO 158 | WP:L&P 159 | WP:MOS 160 | WP:STYLE 161 | WP:ABB 162 | WP:ABBR 163 | WP:MOSBIO 164 | WP:NAMES 165 | WP:MOS-ZH 166 | WP:MOSDATE 167 | WP:MOSNUM 168 | WP:DATE 169 | WP:MOSNUM 170 | WP:MOSDAB 171 | WP:MOSDP 172 | WP:IMOS 173 | WP:MOS-IR 174 | WP:MOS-JA 175 | WP:MOS-JP 176 | WP:MJ 177 | WP:MOS-KO 178 | WP:GTL 179 | WP:LAY 180 | WP:LAYOUT 181 | WP:LEAD 182 | WP:LS 183 | WP:MOS-L 184 | WP:MSM 185 | WP:MOS-MATH 186 | WP:MEDMOS 187 | WP:MOSMED 188 | WP:MOS-P 189 | WP:MOSIPA 190 | WP:PRON 191 | WP:SRTA 192 | WP:SELF 193 | WP:HTUT 194 | WP:MOS-T 195 | WP:MOS-TM 196 | WP:MOSTM 197 | WP:WAF 198 | WP:NC-ZH 199 | WP:N 200 | WP:FICT 201 | WP:MUSIC 202 | WP:MUS 203 | WP:NMG 204 | WP:CORP 205 | WP:ORG 206 | WP:WEB 207 | WP:READER 208 | WP:ROWN 209 | WP:SIG 210 | WP:SIGNATURE 211 | WP:SIGN 212 | WP:NOSPAM 213 | WP:SPAM 214 | WP:SK 215 | WP:SPOIL 216 | WP:SPOILER 217 | WP:SW 218 | WP:STUB 219 | WP:SP 220 | WP:SUBP 221 | WP:SUBPAGE 222 | WP:SUBPAGES 223 | WP:DNUS 224 | WP:DISCUSSION 225 | WP:TALK 226 | WP:TPG 227 | WP:TPT 228 | WP:3O 229 | WP:30 230 | WP:THIRD 231 | WP:RF3O 232 | WP:SISTER 233 | WP:SIS 234 | WP:MOSSIS 235 | -------------------------------------------------------------------------------- /clickthebutton/fonts/JetBrainsMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RealCyGuy/modmail-plugins/b381bd1eeaeacb591d2313cf7a1744f00f5baf53/clickthebutton/fonts/JetBrainsMono-Regular.ttf -------------------------------------------------------------------------------- /clickthebutton/requirements.txt: -------------------------------------------------------------------------------- 1 | matplotlib==3.7.3 2 | brokenaxes==0.5.0 3 | -------------------------------------------------------------------------------- /clickthebutton/silent.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.http import handle_message_parameters 3 | 4 | 5 | async def send_silent(content: str, channel: discord.TextChannel, silent: bool): 6 | """Custom send function for silent messages in discord.py v2.0.1""" 7 | if not silent: 8 | return await channel.send( 9 | content=content, allowed_mentions=discord.AllowedMentions.none() 10 | ) 11 | 12 | if discord.__version__ != "2.0.1": 13 | return await channel.send( 14 | content=content, 15 | allowed_mentions=discord.AllowedMentions.none(), 16 | silent=True, 17 | ) 18 | 19 | # https://github.com/Rapptz/discord.py/blob/v2.0.1/discord/abc.py#L1374 20 | _channel = await channel._get_channel() 21 | state = _channel._state 22 | previous_allowed_mention = state.allowed_mentions 23 | 24 | flags = discord.MessageFlags._from_value(4096) 25 | 26 | with handle_message_parameters( 27 | content=content, 28 | allowed_mentions=discord.AllowedMentions.none(), 29 | previous_allowed_mentions=previous_allowed_mention, 30 | flags=flags, 31 | ) as params: 32 | data = await state.http.send_message(_channel.id, params=params) 33 | 34 | ret = state.create_message(channel=_channel, data=data) 35 | 36 | return ret 37 | -------------------------------------------------------------------------------- /clickthebutton/stats.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Stats: 5 | def __init__(self, streak: List, leaderboard: dict, sorted_leaderboard: List): 6 | self.streak = streak 7 | self.leaderboard = leaderboard 8 | self.sorted_leaderboard = sorted_leaderboard 9 | 10 | @property 11 | def total_clicks(self): 12 | return sum(self.leaderboard.values()) 13 | 14 | @property 15 | def players(self): 16 | return len(self.leaderboard) 17 | -------------------------------------------------------------------------------- /clickthebutton/utils.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | import discord 4 | 5 | 6 | def event(text, content="") -> str: 7 | content = content.split("\n") 8 | content.append(f": {text}") 9 | while len("\n".join(content)) > 2000: 10 | content.pop(0) 11 | return "\n".join(content) 12 | 13 | 14 | def find_data_intervals(values: list, buffer: bool) -> list: 15 | intervals = [] 16 | 17 | for value in values: 18 | if buffer: 19 | intervals.append([value["clicks"][0] - 5, value["clicks"][-1] + 5]) 20 | else: 21 | intervals.append([value["clicks"][0], value["clicks"][-1]]) 22 | 23 | # combine overlapping intervals 24 | merged_intervals = [] 25 | for interval in sorted(intervals): 26 | if not merged_intervals or ( 27 | merged_intervals[-1][1] < interval[0] 28 | and interval[0] - merged_intervals[-1][1] >= 15 29 | ): 30 | merged_intervals.append(interval) 31 | else: 32 | merged_intervals[-1][1] = max(merged_intervals[-1][1], interval[1]) 33 | 34 | return merged_intervals 35 | 36 | 37 | def format_user(user: discord.User) -> str: 38 | if user.discriminator == "0": 39 | return user.name 40 | else: 41 | return f"{user.name}#{user.discriminator}" 42 | -------------------------------------------------------------------------------- /filebackup/filebackup.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import json 3 | 4 | import discord 5 | from discord import DMChannel 6 | from discord.ext import commands 7 | from discord.mixins import Hashable 8 | 9 | from bot import ModmailBot 10 | from core import checks 11 | from core.clients import MongoDBClient 12 | from core.models import PermissionLevel 13 | 14 | 15 | async def append_log_with_backup( 16 | self: MongoDBClient, 17 | message: discord.Message, 18 | *, 19 | message_id: str = "", 20 | channel_id: str = "", 21 | type_: str = "thread_message", 22 | ): 23 | bot: ModmailBot = self.bot 24 | cog = bot.get_cog("FileBackup") 25 | if ( 26 | not message.attachments 27 | or type(cog) is not FileBackup 28 | or not (config := cog.get_config()) 29 | or not (backup_channel_id := config.get("backup_channel")) 30 | or not bot.modmail_guild 31 | or not (backup_channel := bot.modmail_guild.get_channel(backup_channel_id)) 32 | or ( 33 | not config.get("backup_non_staff", True) 34 | and (isinstance(message.channel, DMChannel)) 35 | ) 36 | ): 37 | return await self.old_append_log( 38 | message, message_id=message_id, channel_id=channel_id, type_=type_ 39 | ) 40 | 41 | channel_id = str(channel_id) or str(message.channel.id) 42 | message_id = str(message_id) or str(message.id) 43 | 44 | attachements = [] 45 | for a in message.attachments: 46 | url = a.url 47 | try: 48 | msg = await backup_channel.send( 49 | f"File sent by {message.author.mention} ({message.author.id}) in <#{channel_id}> ({channel_id}).", 50 | file=await a.to_file(), 51 | allowed_mentions=discord.AllowedMentions.none() 52 | ) 53 | url = msg.attachments[0].url 54 | except: 55 | pass 56 | attachements.append( 57 | { 58 | "id": a.id, 59 | "filename": a.filename, 60 | "is_image": a.width is not None, 61 | "size": a.size, 62 | "url": url, 63 | } 64 | ) 65 | 66 | data = { 67 | "timestamp": str(message.created_at), 68 | "message_id": message_id, 69 | "author": { 70 | "id": str(message.author.id), 71 | "name": message.author.name, 72 | "discriminator": message.author.discriminator, 73 | "avatar_url": message.author.display_avatar.url, 74 | "mod": not isinstance(message.channel, DMChannel), 75 | }, 76 | "content": message.content, 77 | "type": type_, 78 | "attachments": attachements, 79 | } 80 | 81 | return await self.logs.find_one_and_update( 82 | {"channel_id": channel_id}, {"$push": {"messages": data}}, return_document=True 83 | ) 84 | 85 | 86 | class FakeChannel(discord.abc.Messageable, discord.abc.GuildChannel, Hashable): 87 | """A fake channel to capture the embed sent by the help command.""" 88 | 89 | def __init__(self): 90 | super().__init__() 91 | self.embed = None 92 | 93 | async def send(self, *args, **kwargs): 94 | self.embed = kwargs["embed"] 95 | 96 | 97 | class FileBackup(commands.Cog): 98 | """ 99 | Automatically backup attachements sent in threads to a Discord channel. 100 | 101 | This is for viewing attachments in the logviewer after the thread channel has been deleted. 102 | """ 103 | 104 | def __init__(self, bot: ModmailBot): 105 | self.bot = bot 106 | self.db = bot.plugin_db.get_partition(self) 107 | self.config = {} 108 | 109 | if not hasattr(MongoDBClient, "old_append_log"): 110 | MongoDBClient.old_append_log = MongoDBClient.append_log 111 | MongoDBClient.append_log = append_log_with_backup 112 | 113 | asyncio.create_task(self._fetch_db()) 114 | 115 | def get_config(self): 116 | return self.config 117 | 118 | async def _update_db(self): 119 | await self.db.find_one_and_update( 120 | {"_id": "config"}, 121 | {"$set": {"config": self.config}}, 122 | upsert=True, 123 | ) 124 | 125 | async def _fetch_db(self): 126 | config = await self.db.find_one({"_id": "config"}) 127 | if config: 128 | self.config = config.get("config", {}) 129 | 130 | @checks.has_permissions(PermissionLevel.ADMIN) 131 | @commands.group(invoke_without_command=True) 132 | async def backupconfig(self, ctx: commands.Context): 133 | """ 134 | Configure FileBackup settings! 135 | 136 | To view your settings, use [p]backupconfig. 137 | To edit, use [p]backupconfig 138 | """ 139 | embed = discord.Embed(colour=self.bot.main_color) 140 | embed.set_author( 141 | name="FileBackup Config", icon_url=self.bot.user.display_avatar.url 142 | ) 143 | embed.description = "```json\n" + json.dumps(self.config, indent=2) + "\n```" 144 | 145 | fake_channel = FakeChannel() 146 | help_command = self.bot.help_command.copy() 147 | help_command.context = ctx 148 | help_command.get_destination = lambda: fake_channel 149 | await help_command.send_group_help(ctx.command) 150 | 151 | await ctx.send(embeds=[embed, fake_channel.embed]) 152 | 153 | @checks.has_permissions(PermissionLevel.ADMIN) 154 | @backupconfig.command() 155 | async def channel( 156 | self, ctx: commands.Context, *, channel: discord.TextChannel = None 157 | ): 158 | """Set the channel where attachements are backed up. Leave empty to disable.""" 159 | if self.bot.modmail_guild != ctx.guild: 160 | return await ctx.send( 161 | "You can only set the backup channel in your modmail guild!" 162 | ) 163 | if channel is None: 164 | self.config.pop("backup_channel", None) 165 | await self._update_db() 166 | await ctx.send( 167 | "Unset backup channel. Files won't be backed up anymore!\n" 168 | f"To set a channel, run `{self.bot.prefix}backupconfig channel `" 169 | ) 170 | else: 171 | self.config["backup_channel"] = channel.id 172 | await self._update_db() 173 | await ctx.send(f"Backup channel set to: `{channel.id}`") 174 | 175 | @checks.has_permissions(PermissionLevel.ADMIN) 176 | @backupconfig.command() 177 | async def nonstaff(self, ctx: commands.Context, *, value: bool): 178 | """Toggle if attachements from non-staff should be backed up. Defaults to true.""" 179 | self.config["backup_non_staff"] = value 180 | await self._update_db() 181 | await ctx.send(f"Backup non-staff set to: `{value}`") 182 | 183 | 184 | async def setup(bot): 185 | await bot.add_cog(FileBackup(bot)) 186 | -------------------------------------------------------------------------------- /fortniteshop/fortniteshop.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import datetime 3 | from collections import defaultdict 4 | 5 | import aiohttp 6 | import discord 7 | from discord.ext import commands, tasks 8 | 9 | from core import checks 10 | from core.models import PermissionLevel 11 | 12 | 13 | def parse_date(date_str: str) -> datetime.datetime: 14 | return datetime.datetime.strptime(date_str, "%Y-%m-%dT%H:%M:%SZ").replace( 15 | tzinfo=datetime.timezone.utc 16 | ) 17 | 18 | 19 | class FortniteShop(commands.Cog): 20 | """Receive the Fortnite item shop in a channel daily.""" 21 | 22 | def __init__(self, bot): 23 | self.bot = bot 24 | self.db = bot.plugin_db.get_partition(self) 25 | 26 | @checks.has_permissions(PermissionLevel.ADMIN) 27 | @commands.command() 28 | async def shopchannel(self, ctx, channel: discord.TextChannel = None): 29 | """ 30 | Set a channel to automatically post the Fortnite shop daily. 31 | """ 32 | self.db.find_one_and_update( 33 | {"_id": "config"}, 34 | {"$set": {"channel": channel.id if channel else None}}, 35 | upsert=True, 36 | ) 37 | if channel: 38 | await ctx.send( 39 | f"Shop channel set to {channel.mention}.\n" 40 | f"Next shop will be sent ." 41 | ) 42 | else: 43 | await ctx.send("Shop channel removed.") 44 | 45 | async def cog_load(self): 46 | self.dailyshop.start() 47 | 48 | async def cog_unload(self): 49 | self.dailyshop.cancel() 50 | 51 | @tasks.loop(time=datetime.time()) 52 | async def dailyshop(self): 53 | config = await self.db.find_one({"_id": "config"}) 54 | if not config: 55 | return 56 | channel_id = config.get("channel") 57 | if not channel_id: 58 | return 59 | channel = self.bot.get_channel(channel_id) 60 | 61 | now = datetime.datetime.now(datetime.timezone.utc) 62 | for attempt in range(20): 63 | async with aiohttp.ClientSession() as session: 64 | async with session.get("https://fortnite-api.com/v2/shop") as resp: 65 | shop = await resp.json() 66 | 67 | updated = parse_date(shop["data"]["date"]) 68 | if now - updated > datetime.timedelta(hours=12): 69 | await asyncio.sleep(attempt) 70 | continue 71 | 72 | cosmetics = defaultdict(list) 73 | ids = set() 74 | for entry in shop["data"]["entries"]: 75 | br_items = entry.get("brItems", []) 76 | if br_items: 77 | for item in br_items: 78 | if item["id"] in ids: 79 | continue 80 | ids.add(item["id"]) 81 | name = item["name"] 82 | name = f"[{name}](https://fnbr.co/{item['type']['value']}/{item['name'].replace(' ', '-').lower()})" 83 | if len(item["shopHistory"]) == 1: 84 | name = f"__**{name}**__" 85 | days = 999999999 86 | else: 87 | diff = updated - parse_date(item["shopHistory"][-2]) 88 | days = diff.days 89 | if diff > datetime.timedelta(hours=25): 90 | if days > 300: 91 | name += f" (**{days}**)" 92 | else: 93 | name += f" ({days})" 94 | cosmetics[item["type"]["value"].capitalize() + "s"].append( 95 | (name, days) 96 | ) 97 | 98 | def create_embed() -> discord.Embed: 99 | em = discord.Embed(colour=0x2B2D31) 100 | em.set_footer(text="Updated") 101 | em.timestamp = updated 102 | return em 103 | 104 | embeds = [create_embed()] 105 | keys = ["Outfits", "Emotes", "Pickaxes", "Gliders", "Backpacks"] 106 | for key in keys: 107 | values = cosmetics.get(key, []) 108 | pages = 0 109 | page = [] 110 | length = 0 111 | for name, _ in sorted(values, key=lambda x: x[1], reverse=True): 112 | if len(page) > 0: 113 | length += 2 114 | length += len(name) 115 | if length > 1024: 116 | embeds[-1].add_field( 117 | name=key, value=", ".join(page), inline=False 118 | ) 119 | if len(embeds[-1]) > 6000: 120 | embeds[-1].remove_field(-1) 121 | embeds.append(create_embed()) 122 | embeds[-1].add_field( 123 | name=key, value=", ".join(page), inline=False 124 | ) 125 | if pages == 0: 126 | key = f"{key} continued" 127 | pages += 1 128 | length = len(name) 129 | page = [] 130 | page.append(name) 131 | if page: 132 | embeds[-1].add_field(name=key, value=", ".join(page), inline=False) 133 | if len(embeds[-1]) > 6000: 134 | embeds[-1].remove_field(-1) 135 | embeds.append(create_embed()) 136 | embeds[-1].add_field( 137 | name=key, value=", ".join(page), inline=False 138 | ) 139 | for i, embed in enumerate(embeds): 140 | if i + 1 != len(embeds): 141 | embed.remove_footer() 142 | embed.timestamp = None 143 | await channel.send(embed=embed) 144 | break 145 | 146 | 147 | async def setup(bot): 148 | await bot.add_cog(FortniteShop(bot)) 149 | -------------------------------------------------------------------------------- /hotreload/hotreload.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | from discord.ext import commands 4 | from watchfiles import awatch 5 | 6 | from core import checks 7 | from core.models import PermissionLevel, getLogger 8 | 9 | logger = getLogger(__name__) 10 | 11 | 12 | class HotReload(commands.Cog): 13 | """Hot-reload local plugins for development!""" 14 | 15 | def __init__(self, bot): 16 | self.bot = bot 17 | self.plugins = {} 18 | 19 | async def watch_plugin(self, plugin: str): 20 | async for _ in awatch(Path("plugins") / "@local" / plugin, debounce=2000): 21 | extension = f"plugins.@local.{plugin}.{plugin}" 22 | try: 23 | await self.bot.reload_extension(extension) 24 | except Exception as e: 25 | logger.info(f"Failed to reload {extension}: {e}") 26 | else: 27 | logger.info(f"Reloaded {extension}.") 28 | 29 | @checks.has_permissions(PermissionLevel.OWNER) 30 | @commands.command() 31 | async def hotreload(self, ctx, plugin: str = None): 32 | """Start watching a local plugin for hot-reloading.""" 33 | if plugin is None: 34 | return await ctx.send("Currently watching: " + ", ".join(self.plugins)) 35 | 36 | if plugin in self.plugins: 37 | self.plugins[plugin].cancel() 38 | del self.plugins[plugin] 39 | return await ctx.send(f"Stopped watching `@local/{plugin}`.") 40 | 41 | if f"plugins.@local.{plugin}.{plugin}" not in self.bot.extensions: 42 | return await ctx.send( 43 | f"Plugin `@local/{plugin}` does not exist/is not loaded." 44 | ) 45 | 46 | task = self.bot.loop.create_task(self.watch_plugin(plugin)) 47 | self.plugins[plugin] = task 48 | await ctx.send(f"Watching `@local/{plugin}`.") 49 | 50 | 51 | async def setup(bot): 52 | await bot.add_cog(HotReload(bot)) 53 | -------------------------------------------------------------------------------- /hotreload/requirements.txt: -------------------------------------------------------------------------------- 1 | watchfiles==0.19.0 2 | -------------------------------------------------------------------------------- /karutakeqingautoreact/karutakeqingautoreact.py: -------------------------------------------------------------------------------- 1 | import unicodedata 2 | from typing import List 3 | 4 | import discord 5 | from discord.ext import commands 6 | 7 | 8 | async def add_reactions(message: discord.Message, reactions: List[str]) -> None: 9 | for reaction in reactions: 10 | try: 11 | emoji = unicodedata.lookup(reaction) 12 | except KeyError: 13 | emoji = reaction 14 | try: 15 | await message.add_reaction(emoji) 16 | except discord.Forbidden: 17 | break 18 | 19 | 20 | class KarutaKeqingAutoReact(commands.Cog): 21 | """ 22 | Automatically add reactions to Karuta messages for Keqing Bot that usually requires premium. 23 | """ 24 | 25 | def __init__(self, bot): 26 | self.bot = bot 27 | 28 | @commands.Cog.listener() 29 | async def on_message(self, message: discord.Message): 30 | if message.author.id == 646937666251915264 and message.embeds: 31 | embed = message.embeds[0] 32 | if embed.title: 33 | if embed.title in ["Character Lookup", "Character Results"]: 34 | await add_reactions(message, ["🖌️", "Money Bag", "Revolving Hearts", "🎖️"]) 35 | if embed.author: 36 | if embed.author.name.startswith("Card Collection"): 37 | await add_reactions(message, ["Left-Pointing Magnifying Glass", "Memo"]) 38 | elif embed.author.name.startswith("Bits"): 39 | await add_reactions(message, ["Heavy Plus Sign"]) 40 | 41 | 42 | 43 | async def setup(bot): 44 | await bot.add_cog(KarutaKeqingAutoReact(bot)) 45 | -------------------------------------------------------------------------------- /marketgraph/marketgraph.py: -------------------------------------------------------------------------------- 1 | import io 2 | import typing 3 | from datetime import datetime, timedelta 4 | 5 | import discord 6 | import numpy as np 7 | import pandas as pd 8 | import seaborn as sns 9 | from discord.ext import commands 10 | import matplotlib as mpl 11 | from matplotlib import pyplot as plt 12 | from matplotlib.dates import AutoDateLocator 13 | from motor.core import AgnosticCollection 14 | from motor.motor_asyncio import AsyncIOMotorClient 15 | from pymongo.collation import Collation, CollationStrength 16 | from pymongo.errors import InvalidName, ConfigurationError 17 | from scipy import stats 18 | 19 | from core import checks 20 | from core.models import PermissionLevel 21 | from core.time import human_timedelta 22 | 23 | 24 | class MarketGraph(commands.Cog): 25 | """ 26 | Commands that are only useful with a specific MongoDB database schema: 27 | ```json 28 | { 29 | "date": date 30 | "item": { 31 | "name": str, 32 | "amount": int 33 | }, 34 | "price": { 35 | "name": str, 36 | "amount": int 37 | }, 38 | "rate": float 39 | } 40 | ``` 41 | """ 42 | 43 | def __init__(self, bot): 44 | self.bot = bot 45 | self.plugin_db = bot.plugin_db.get_partition(self) 46 | self._db: typing.Optional[AgnosticCollection] = None 47 | 48 | async def db(self, ctx: commands.Context) -> typing.Optional[AgnosticCollection]: 49 | if self._db: 50 | return self._db 51 | try: 52 | config = await self.plugin_db.find_one( 53 | {"_id": "config"}, 54 | ) 55 | uri = config["uri"] 56 | database = config["database"] 57 | collection = config["collection"] 58 | self._db = AsyncIOMotorClient(uri)[database][collection] 59 | except (TypeError, KeyError, InvalidName, ConfigurationError): 60 | await ctx.send( 61 | "Your mongo database details have been set incorrectly. Run `?setmarketgraph`." 62 | ) 63 | return 64 | else: 65 | return self._db 66 | 67 | @commands.command() 68 | @checks.has_permissions(PermissionLevel.ADMIN) 69 | async def setmarketgraph(self, ctx, uri: str, database: str, collection: str): 70 | """ 71 | Set the mongo uri, database name, and collection name of the database. 72 | """ 73 | await self.plugin_db.find_one_and_update( 74 | {"_id": "config"}, 75 | {"$set": {"uri": uri, "database": database, "collection": collection}}, 76 | upsert=True, 77 | ) 78 | self._db = None 79 | await ctx.send("Set market graph stuff.") 80 | 81 | @commands.command() 82 | @checks.has_permissions(PermissionLevel.REGULAR) 83 | async def graph(self, ctx, days: typing.Optional[float] = 14, *, price_item: str): 84 | """ 85 | Graph the price/item rate. 86 | """ 87 | split = price_item.lower().split("/") 88 | if len(split) != 2: 89 | return await ctx.send("Use a `/` to separate the price and item.") 90 | price = split[0].strip() 91 | item = split[1].strip() 92 | db = await self.db(ctx) 93 | if not db: 94 | return 95 | mpl.rcParams.update(mpl.rcParamsDefault) 96 | sns.set_style("darkgrid") 97 | sns.set_palette("deep") 98 | query = {"price.name": price, "item.name": item} 99 | title = f"{price}/{item} rate" 100 | if days > 0: 101 | date = datetime.utcnow() - timedelta(days=days) 102 | query["date"] = {"$gte": date} 103 | title += f" since {human_timedelta(date)}" 104 | data = pd.DataFrame( 105 | await db.find( 106 | query, 107 | collation=Collation("en_US", strength=CollationStrength.SECONDARY), 108 | ).to_list(length=None) 109 | ) 110 | if len(data) == 0: 111 | await ctx.reply("No data found.") 112 | return 113 | sub = data.loc[:, "rate"] 114 | data.loc[:, "rate"] = sub.where( 115 | np.logical_and( 116 | sub < sub.quantile(0.99), 117 | sub > sub.quantile(0.01), 118 | ), 119 | np.nan, 120 | ) 121 | data.dropna(subset="rate", inplace=True) 122 | means = data.resample("D", on="date")["rate"].mean().to_frame() 123 | x = "date" 124 | y = "rate" 125 | g = sns.JointGrid(data=data, x=x, y=y, space=0.1) 126 | g.fig.subplots_adjust(top=0.947) 127 | g.fig.suptitle(title, x=0.07, y=0.97, ha="left", fontsize=20) 128 | g.fig.set_figwidth(20) 129 | g.fig.set_figheight(14) 130 | values = np.vstack([data["date"].values.astype("float64"), data["rate"]]) 131 | kernel = stats.gaussian_kde(values)(values) 132 | g.plot_joint(sns.kdeplot, fill=True, alpha=0.5) 133 | g.plot_joint(sns.scatterplot, s=70, c=kernel, cmap="magma", edgecolors=(1, 1, 1, 0.6)) 134 | sns.lineplot(means, x=x, y=y, ax=g.ax_joint, lw=3, alpha=0.7) 135 | g.plot_marginals(sns.histplot, kde=True) 136 | g.ax_joint.xaxis.set_major_locator( 137 | locator=AutoDateLocator(maxticks=70, interval_multiples=False) 138 | ) 139 | for label in g.ax_joint.get_xticklabels(): 140 | label.set_rotation(90) 141 | buffer = io.BytesIO() 142 | g.savefig( 143 | buffer, 144 | format="png", 145 | ) 146 | buffer.seek(0) 147 | plt.close(g.figure) 148 | file = discord.File(buffer, filename="graph.png") 149 | await ctx.send(file=file) 150 | 151 | 152 | async def setup(bot): 153 | await bot.add_cog(MarketGraph(bot)) 154 | -------------------------------------------------------------------------------- /marketgraph/requirements.txt: -------------------------------------------------------------------------------- 1 | matplotlib==3.7.3 2 | seaborn 3 | scipy 4 | -------------------------------------------------------------------------------- /message/message.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands, tasks 3 | 4 | import asyncio 5 | 6 | from core import checks 7 | from core.models import PermissionLevel 8 | from core.paginator import EmbedPaginatorSession 9 | 10 | import datetime 11 | 12 | 13 | class MessageManager(commands.Cog): 14 | """ 15 | A plugin that... manages messages. 16 | 17 | It also has cool message-managing stuff. 18 | """ 19 | 20 | def __init__(self, bot): 21 | self.bot = bot 22 | self.db = bot.plugin_db.get_partition(self) 23 | 24 | self.decay_channels = dict() 25 | 26 | self.decay_loop.start() 27 | asyncio.create_task(self._set_val()) 28 | 29 | async def _update_db(self): 30 | await self.db.find_one_and_update( 31 | {"_id": "config"}, 32 | {"$set": {"decay-channel": self.decay_channels,}}, 33 | upsert=True, 34 | ) 35 | 36 | async def _set_val(self): 37 | config = await self.db.find_one({"_id": "config"}) 38 | 39 | if config is None: 40 | return 41 | 42 | self.decay_channels = config["decay-channel"] 43 | 44 | @checks.has_permissions(PermissionLevel.MOD) 45 | @commands.command() 46 | async def clear(self, ctx, amount: int): 47 | """Clear messages.""" 48 | if amount < 1: 49 | await ctx.send( 50 | embed=discord.Embed( 51 | title=f"{amount} is too small! Please try again.", 52 | colour=self.bot.error_color, 53 | ) 54 | ) 55 | else: 56 | deleted_messages = await ctx.channel.purge(limit=amount + 1) 57 | message_number = max(len(deleted_messages) - 1, 0) 58 | 59 | if message_number == 0: 60 | embed = discord.Embed( 61 | title="No messages deleted.", colour=self.bot.error_color, 62 | ) 63 | else: 64 | letter_s = "" if message_number < 2 else "s" 65 | embed = discord.Embed( 66 | title=f"I have deleted {message_number} message{letter_s}!", 67 | colour=self.bot.main_color, 68 | ) 69 | 70 | confirm = await ctx.send(embed=embed) 71 | await asyncio.sleep(8) 72 | await confirm.delete() 73 | 74 | @checks.has_permissions(PermissionLevel.ADMIN) 75 | @commands.group(aliases=["aclear"], invoke_without_command=True) 76 | async def advancedclear(self, ctx): 77 | """ 78 | Clearing messages, but advanced. 79 | """ 80 | await ctx.send_help(ctx.command) 81 | 82 | @checks.has_permissions(PermissionLevel.ADMIN) 83 | @advancedclear.command() 84 | async def contains(self, ctx, *, text): 85 | def is_in(m): 86 | return text.lower() in m.content 87 | 88 | deleted_messages = await ctx.channel.purge(check=is_in) 89 | message_number = max(len(deleted_messages) - 1, 0) 90 | 91 | if message_number == 0: 92 | embed = discord.Embed( 93 | title="No messages deleted.", colour=self.bot.error_color, 94 | ) 95 | else: 96 | letter_s = "" if message_number < 2 else "s" 97 | embed = discord.Embed( 98 | title=f"I have deleted {message_number} message{letter_s}!", 99 | colour=self.bot.main_color, 100 | ) 101 | 102 | confirm = await ctx.send(embed=embed) 103 | await asyncio.sleep(8) 104 | await confirm.delete() 105 | 106 | @checks.has_permissions(PermissionLevel.ADMIN) # TODO: Set your own decay time. 107 | @commands.command() 108 | async def decay(self, ctx, channel: discord.TextChannel): 109 | """ 110 | Deletes messages after some time in a channel except for pinned ones. 111 | 112 | Right now, the only time is 1 day. 113 | The only way to change it currently is through editing the database directly. 114 | """ 115 | if str(channel.id) in self.decay_channels: 116 | self.decay_channels.pop(str(channel.id)) 117 | msg = f"Stopped decaying in #{channel.name}." 118 | else: 119 | self.decay_channels[str(channel.id)] = 86400000 120 | msg = f"Decaying in #{channel.name}!" 121 | 122 | await self._update_db() 123 | await ctx.send(msg) 124 | 125 | @checks.has_permissions(PermissionLevel.ADMIN) 126 | @commands.command() 127 | async def decayinfo(self, ctx): 128 | if self.decay_channels: 129 | pages = [] 130 | total = 0 131 | 132 | for channel in self.decay_channels: 133 | total += self.decay_channels[channel] 134 | 135 | average = total / len(self.decay_channels) 136 | 137 | front = discord.Embed(color=self.bot.main_color, title="All decay info.") 138 | front.add_field( 139 | name="Decay channels:", 140 | value=str(len(self.decay_channels)), 141 | inline=True, 142 | ) 143 | front.add_field( 144 | name="Average decay time:", value=f"{str(average)}ms", inline=True, 145 | ) 146 | front.add_field( 147 | name="To see channel specific info, use the reactions below.", 148 | value="\u200b", 149 | inline=False, 150 | ) 151 | pages.append(front) 152 | 153 | for channel in self.decay_channels: 154 | d_channel = self.bot.get_channel(int(channel)) 155 | page = discord.Embed( 156 | color=self.bot.main_color, title=f"Decay info of: #{d_channel.name}" 157 | ) 158 | page.add_field( 159 | name="Decay time:", value=f"{str(self.decay_channels[channel])}ms" 160 | ) 161 | 162 | pages.append(page) 163 | 164 | session = EmbedPaginatorSession(ctx, *pages) 165 | await session.run() 166 | 167 | else: 168 | embed = discord.Embed( 169 | color=self.bot.error_color, 170 | title="No channels are decaying, to decay a channel use the command: `[p]decay #channel`", 171 | ) 172 | await ctx.send(embed=embed) 173 | 174 | @tasks.loop(seconds=5.0) 175 | async def decay_loop(self): 176 | def is_deleteable(m): 177 | time_diff = datetime.datetime.now() - m.created_at 178 | return not m.pinned and time_diff > delta 179 | 180 | if self.decay_channels: 181 | for channel in self.decay_channels: 182 | delta = datetime.timedelta(milliseconds=self.decay_channels[channel]) 183 | d_channel = self.bot.get_channel(int(channel)) 184 | 185 | await d_channel.purge(check=is_deleteable) 186 | 187 | 188 | async def setup(bot): 189 | await bot.add_cog(MessageManager(bot)) 190 | -------------------------------------------------------------------------------- /plugins.json: -------------------------------------------------------------------------------- 1 | { 2 | "allowed":[ 3 | "suggest" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /premiumsupport/premiumsupport.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import datetime 3 | 4 | import discord 5 | from discord.ext import commands 6 | 7 | from bot import ModmailBot 8 | from core import checks 9 | from core.models import PermissionLevel 10 | 11 | 12 | class PremiumSupport(commands.Cog): 13 | """Special support for Premium members.""" 14 | 15 | def __init__(self, bot: ModmailBot): 16 | self.bot = bot 17 | self.db = bot.plugin_db.get_partition(self) 18 | 19 | self.roles = [] 20 | self.message = "" 21 | self.mention = "" 22 | self.category = 0 23 | 24 | asyncio.create_task(self._set_val()) 25 | 26 | async def _update_db(self): 27 | await self.db.find_one_and_update( 28 | {"_id": "config"}, 29 | { 30 | "$set": { 31 | "roles": self.roles, 32 | "message": self.message, 33 | "mention": self.mention, 34 | "category": self.category, 35 | } 36 | }, 37 | upsert=True, 38 | ) 39 | 40 | async def _set_val(self): 41 | config = await self.db.find_one({"_id": "config"}) 42 | 43 | if config: 44 | self.roles = config.get("roles", []) 45 | self.message = config.get("message", "") 46 | self.mention = config.get("mention", "") 47 | self.category = config.get("category", "") 48 | 49 | @commands.Cog.listener() 50 | async def on_thread_ready(self, thread, creator, category, initial_message): 51 | if isinstance(thread.recipient, int): 52 | recipient_id = thread.recipient 53 | else: 54 | recipient_id = thread.recipient.id 55 | recipient = await self.bot.modmail_guild.fetch_member(recipient_id) 56 | premium = False 57 | for role in recipient.roles: 58 | if role.id in self.roles: 59 | premium = True 60 | 61 | if not premium: 62 | return 63 | 64 | class Author: 65 | roles = [] 66 | id = recipient_id 67 | 68 | class Msg: 69 | content = self.message 70 | author = Author 71 | created_at = datetime.datetime.now() 72 | id = initial_message.id 73 | attachments = [] 74 | stickers = [] 75 | 76 | if Msg.content: 77 | await thread.send(Msg, destination=recipient, from_mod=True, anonymous=True) 78 | 79 | if self.mention: 80 | await thread.channel.send(self.mention) 81 | 82 | if self.category: 83 | await thread.channel.move( 84 | end=True, 85 | category=discord.utils.get( 86 | thread.channel.guild.channels, id=self.category 87 | ), 88 | reason="Premium support plugin.", 89 | ) 90 | 91 | @checks.has_permissions(PermissionLevel.ADMIN) 92 | @commands.group(invoke_without_command=True, aliases=["pc"]) 93 | async def premiumconfig(self, ctx): 94 | """ 95 | Config your premium support! 96 | 97 | To view your settings, use [p]premiumconfig. 98 | 99 | To edit, use [p]premiumconfig 100 | """ 101 | embed = discord.Embed(colour=self.bot.main_color) 102 | embed.set_author( 103 | name="Premium Support Configurations:", icon_url=self.bot.user.avatar.url 104 | ) 105 | embed.add_field(name="Premium Roles", value=f"`{self.roles}`", inline=False) 106 | embed.add_field( 107 | name="Premium Message", 108 | value=f"{'`' + self.message + '`' if self.message else 'None'}", 109 | inline=False, 110 | ) 111 | embed.add_field( 112 | name="Mention Message", 113 | value=f"{'`' + self.mention + '`' if self.message else 'None'}", 114 | inline=False, 115 | ) 116 | embed.add_field( 117 | name="Premium Category", value=f"`{self.category}`", inline=False 118 | ) 119 | embed.set_footer( 120 | text=f"To change use {self.bot.prefix}premiumconfig . Use {self.bot.prefix}help premiumconfig for the list of things you can change." 121 | ) 122 | await ctx.send(embed=embed) 123 | 124 | @checks.has_permissions(PermissionLevel.ADMIN) 125 | @premiumconfig.command(aliases=["role"]) 126 | async def roles(self, ctx, roles: commands.Greedy[discord.Role]): 127 | """Set premium roles.""" 128 | self.roles = [role.id for role in roles] 129 | await self._update_db() 130 | await ctx.send(f"Premium roles set to: `{self.roles}`") 131 | 132 | @checks.has_permissions(PermissionLevel.ADMIN) 133 | @premiumconfig.command() 134 | async def message(self, ctx, *, message): 135 | """Set premium message reply.""" 136 | self.message = message 137 | await self._update_db() 138 | await ctx.send(f"Premium message set to: `{self.message}`") 139 | 140 | @checks.has_permissions(PermissionLevel.ADMIN) 141 | @premiumconfig.command() 142 | async def mention(self, ctx, *, message): 143 | """Set on_premium mention message.""" 144 | self.mention = message 145 | await self._update_db() 146 | await ctx.send(f"Mention message set to: `{self.mention}`") 147 | 148 | @checks.has_permissions(PermissionLevel.ADMIN) 149 | @premiumconfig.command() 150 | async def category(self, ctx, category_id: int = 0): 151 | """Set premium category id. 0 equals none.""" 152 | self.category = category_id 153 | await self._update_db() 154 | await ctx.send(f"Premium category set to: `{self.category}`") 155 | 156 | 157 | async def setup(bot): 158 | await bot.add_cog(PremiumSupport(bot)) 159 | -------------------------------------------------------------------------------- /randomvclimit/randomvclimit.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | import discord 4 | from discord.ext import commands, tasks 5 | 6 | from core import checks 7 | from core.models import PermissionLevel 8 | 9 | 10 | class RandomVCLimit(commands.Cog): 11 | """Automatically set a voice channel's member limit to a random value.""" 12 | 13 | def __init__(self, bot): 14 | self.bot = bot 15 | self.db = bot.plugin_db.get_partition(self) 16 | self.channels = set() 17 | 18 | async def _update_db(self): 19 | await self.db.find_one_and_update( 20 | {"_id": "config"}, {"$set": {"channels": list(self.channels)}}, upsert=True 21 | ) 22 | 23 | async def _get_db(self): 24 | config = await self.db.find_one({"_id": "config"}) 25 | if config is None: 26 | return 27 | self.channels = set(config.get("channels", [])) 28 | 29 | async def cog_load(self): 30 | await self._get_db() 31 | self.random_limit_loop.start() 32 | 33 | async def cog_unload(self): 34 | self.random_limit_loop.cancel() 35 | 36 | @checks.has_permissions(PermissionLevel.ADMIN) 37 | @commands.group(invoke_without_command=True) 38 | async def randomvc(self, ctx): 39 | """Get a list of random vcs.""" 40 | await ctx.send( 41 | "Channels: " 42 | + ", ".join([f"`{c}` (<#{c.split('-')[1]}>)" for c in self.channels]) 43 | + f"\nNext modification is ." 44 | ) 45 | 46 | @checks.has_permissions(PermissionLevel.ADMIN) 47 | @randomvc.command() 48 | async def add(self, ctx, *, channel: discord.VoiceChannel): 49 | """Set the voice channel to be configured.""" 50 | self.channels.add(str(ctx.guild.id) + "-" + str(channel.id)) 51 | await self._update_db() 52 | await ctx.send( 53 | f"Added {channel.mention} to random vc list. It will be modified ." 54 | ) 55 | 56 | @checks.has_permissions(PermissionLevel.ADMIN) 57 | @randomvc.command() 58 | async def remove(self, ctx, channel: str): 59 | """Remove a voice channel from the list.""" 60 | try: 61 | self.channels.remove(channel) 62 | except KeyError: 63 | return await ctx.send("That channel is not in the list.") 64 | await self._update_db() 65 | await ctx.send(f"Removed {channel} from random vc list.") 66 | 67 | @tasks.loop(minutes=11) 68 | async def random_limit_loop(self): 69 | for channel in self.channels: 70 | guild_id, channel_id = channel.split("-") 71 | guild = self.bot.get_guild(int(guild_id)) 72 | if guild is None: 73 | continue 74 | channel = guild.get_channel(int(channel_id)) 75 | if channel is None: 76 | continue 77 | try: 78 | await channel.edit(user_limit=random.randint(2, 99)) 79 | except Exception: 80 | pass 81 | 82 | 83 | async def setup(bot): 84 | await bot.add_cog(RandomVCLimit(bot)) 85 | -------------------------------------------------------------------------------- /react-on-ping/react-on-ping.py: -------------------------------------------------------------------------------- 1 | from discord.ext import commands 2 | 3 | 4 | class ReactOnPing(commands.Cog): 5 | """Reacts with a ping emoji when someone gets pinged.""" 6 | 7 | emojis = ["🇵", "🇮", "🇳", "🇬"] 8 | 9 | def __init__(self, bot): 10 | self.bot = bot 11 | 12 | @commands.Cog.listener() 13 | async def on_message(self, message): 14 | if len(message.mentions): 15 | for emoji in self.emojis: 16 | await message.add_reaction(emoji) 17 | 18 | 19 | async def setup(bot): 20 | await bot.add_cog(ReactOnPing(bot)) 21 | -------------------------------------------------------------------------------- /remove-self-stars/remove-self-stars.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | 4 | import logging 5 | 6 | logger = logging.getLogger("Modmail") 7 | 8 | 9 | class RemoveSelfStars(commands.Cog): 10 | """Removes self-stars for starboard.""" 11 | 12 | def __init__(self, bot): 13 | self.bot = bot 14 | 15 | @commands.Cog.listener() 16 | async def on_raw_reaction_add(self, payload): 17 | if str(payload.emoji) != "\N{WHITE MEDIUM STAR}": 18 | return 19 | 20 | channel = self.bot.get_channel(payload.channel_id) 21 | message = await channel.fetch_message(payload.message_id) 22 | 23 | user = self.bot.get_user(payload.user_id) 24 | if user is None or user.bot: 25 | return 26 | 27 | if payload.user_id == message.author.id: 28 | try: 29 | await message.remove_reaction("\N{WHITE MEDIUM STAR}", user) 30 | logger.info( 31 | f"I removed a self star from {user.name}#{user.discriminator}." 32 | ) 33 | except discord.Forbidden: 34 | logger.error( 35 | f"I didn't have permissions to remove a self star from {user.name}#{user.discriminator}." 36 | ) 37 | 38 | 39 | async def setup(bot): 40 | await bot.add_cog(RemoveSelfStars(bot)) 41 | -------------------------------------------------------------------------------- /say/say.py: -------------------------------------------------------------------------------- 1 | from discord.ext import commands 2 | 3 | 4 | class Say(commands.Cog): 5 | """A simple say command that removes everyone/here mentions.""" 6 | 7 | def __init__(self, bot): 8 | self.bot = bot 9 | 10 | @commands.command() 11 | async def say2(self, ctx, *, message): 12 | """Modmail says what you want it to say.""" 13 | await ctx.send( 14 | message.replace("@everyone", "@\u200beveryone").replace( 15 | "@here", "@\u200bhere" 16 | ) 17 | ) 18 | await ctx.message.delete() 19 | 20 | 21 | async def setup(bot): 22 | await bot.add_cog(Say(bot)) 23 | -------------------------------------------------------------------------------- /suggest/suggest.py: -------------------------------------------------------------------------------- 1 | # registry: suggest 2 | 3 | import discord 4 | from discord.ext import commands 5 | from core import checks 6 | from core.models import PermissionLevel 7 | import asyncio 8 | 9 | 10 | class Suggest(commands.Cog): 11 | """ 12 | Let's you send a suggestion to a designated channel. 13 | """ 14 | 15 | def __init__(self, bot): 16 | self.bot = bot 17 | self.coll = bot.plugin_db.get_partition(self) 18 | 19 | self.banlist = dict() 20 | 21 | bot.loop.create_task(self._set_mod_val()) 22 | 23 | async def _update_mod_db(self): 24 | await self.coll.find_one_and_update( 25 | {"_id": "mod"}, {"$set": {"banlist": self.banlist}}, upsert=True, 26 | ) 27 | 28 | async def _set_mod_val(self): 29 | mod = await self.coll.find_one({"_id": "mod"}) 30 | 31 | if mod is None: 32 | return 33 | 34 | self.banlist = mod["banlist"] 35 | 36 | @commands.command() 37 | @commands.cooldown(1, 20, commands.BucketType.member) 38 | @checks.has_permissions(PermissionLevel.REGULAR) 39 | async def suggest(self, ctx, *, suggestion): 40 | """ 41 | Suggest something! 42 | 43 | **Usage**: 44 | [p]suggest more plugins! 45 | """ 46 | if str(ctx.author.id) not in self.banlist: 47 | async with ctx.channel.typing(): 48 | config = await self.coll.find_one({"_id": "config"}) 49 | if config is None: 50 | embed = discord.Embed( 51 | title="Suggestion channel not set.", color=self.bot.error_color 52 | ) 53 | embed.set_author(name="Error.") 54 | embed.set_footer(text="Task failed successfully.") 55 | await ctx.send(embed=embed) 56 | else: 57 | suggestion_channel = self.bot.get_channel( 58 | int(config["suggestion-channel"]["channel"]) 59 | ) 60 | suggestions = await self.coll.find_one({"_id": "suggestions"}) or {} 61 | next_id = suggestions.get("next_id", 1) 62 | 63 | embed = discord.Embed(color=0x59E9FF) 64 | embed.set_author(name=f"Suggestion #{next_id}: Waiting") 65 | embed.set_thumbnail(url=ctx.author.avatar.url) 66 | embed.add_field( 67 | name="Author", 68 | value=f"{ctx.author.mention} (ID: {ctx.author.id})", 69 | inline=False, 70 | ) 71 | embed.add_field(name="Suggestion", value=suggestion, inline=False) 72 | message = await suggestion_channel.send(embed=embed) 73 | await self.coll.find_one_and_update( 74 | {"_id": "suggestions"}, 75 | { 76 | "$set": { 77 | "next_id": next_id + 1, 78 | str(next_id): {"message_id": message.id,}, 79 | } 80 | }, 81 | upsert=True, 82 | ) 83 | re = config.get("reaction-emojis") 84 | if re: 85 | for r in re.get("emojis", []): 86 | await message.add_reaction( 87 | discord.utils.get(message.guild.emojis, id=r) 88 | ) 89 | await asyncio.sleep(0.1) 90 | await ctx.message.add_reaction("\N{WHITE HEAVY CHECK MARK}") 91 | else: 92 | await ctx.send( 93 | embed=discord.Embed( 94 | color=self.bot.error_color, 95 | title=f"You have been blocked, {ctx.author.name}#{ctx.author.discriminator}.", 96 | description=f"Reason: {self.banlist[str(ctx.author.id)]}", 97 | ) 98 | ) 99 | 100 | @commands.command() 101 | @checks.has_permissions(PermissionLevel.ADMIN) 102 | async def approve(self, ctx, suggestion_id: int, *, message=None): 103 | """ 104 | Approve an suggestion. 105 | 106 | **Usage**: 107 | [p]approve 5 That's a good idea and will be implemented soon (trademarked). 108 | [p]approve 456 I agree. 109 | """ 110 | await ctx.message.delete() 111 | suggestions = await self.coll.find_one({"_id": "suggestions"}) 112 | suggestion = suggestions.get(str(suggestion_id), None) 113 | if not suggestion: 114 | embed = discord.Embed( 115 | colour=self.bot.error_color, 116 | title=f"Suggestion id #{suggestion_id} not found.", 117 | description="Try something else lol.", 118 | ) 119 | return await ctx.send(embed=embed) 120 | s_message = None 121 | for channel in ctx.guild.channels: 122 | if not isinstance(channel, discord.TextChannel): 123 | continue 124 | try: 125 | s_message = await channel.fetch_message(suggestion["message_id"]) 126 | except discord.NotFound: 127 | continue 128 | if not s_message: 129 | embed = discord.Embed( 130 | colour=self.bot.error_color, 131 | title=f"Message not found.", 132 | description="Make sure it's not deleted and that I have permissions to access it.", 133 | ) 134 | return await ctx.send(embed=embed) 135 | embed = s_message.embeds[0] 136 | fields = len(embed.fields) 137 | embed.color = discord.Colour.green() 138 | embed.set_author(name=f"Suggestion #{suggestion_id}: Approved") 139 | if fields > 2: 140 | embed.remove_field(2) 141 | if fields == 4: 142 | embed.insert_field_at( 143 | index=2, 144 | name="Response", 145 | value=message if message else "No response given.", 146 | inline=False, 147 | ) 148 | else: 149 | embed.add_field( 150 | name="Response", 151 | value=message if message else "No response given.", 152 | inline=False, 153 | ) 154 | votes = "" 155 | for reaction in s_message.reactions: 156 | votes += f"{reaction.emoji} **- {reaction.count -1 if reaction.me else reaction.count }**\n" 157 | if votes: 158 | embed.add_field(name="Votes", value=votes, inline=False) 159 | await s_message.edit(embed=embed) 160 | await s_message.clear_reactions() 161 | 162 | @commands.command() 163 | @checks.has_permissions(PermissionLevel.ADMIN) 164 | async def deny(self, ctx, suggestion_id: int, *, message=None): 165 | """ 166 | Deny an suggestion. 167 | 168 | **Usage**: 169 | [p]deny 27 That wouldn't work due to the way the thing in the works in contrast with the other thing. 170 | [p]deny 78 You're stupid. 171 | """ 172 | await ctx.message.delete() 173 | suggestions = await self.coll.find_one({"_id": "suggestions"}) 174 | suggestion = suggestions.get(str(suggestion_id), None) 175 | if not suggestion: 176 | embed = discord.Embed( 177 | colour=self.bot.error_color, 178 | title=f"Suggestion id #{suggestion_id} not found.", 179 | description="Try something else lol.", 180 | ) 181 | return await ctx.send(embed=embed) 182 | s_message = None 183 | for channel in ctx.guild.channels: 184 | if not isinstance(channel, discord.TextChannel): 185 | continue 186 | try: 187 | s_message = await channel.fetch_message(suggestion["message_id"]) 188 | except discord.NotFound: 189 | continue 190 | if not s_message: 191 | embed = discord.Embed( 192 | colour=self.bot.error_color, 193 | title=f"Message not found.", 194 | description="Make sure it's not deleted and that I have permissions to access it.", 195 | ) 196 | return await ctx.send(embed=embed) 197 | embed = s_message.embeds[0] 198 | fields = len(embed.fields) 199 | embed.color = discord.Colour.red() 200 | embed.set_author(name=f"Suggestion #{suggestion_id}: Denied") 201 | if fields > 2: 202 | embed.remove_field(2) 203 | if fields == 4: 204 | embed.insert_field_at( 205 | index=2, 206 | name="Response", 207 | value=message if message else "No response given.", 208 | inline=False, 209 | ) 210 | else: 211 | embed.add_field( 212 | name="Response", 213 | value=message if message else "No response given.", 214 | inline=False, 215 | ) 216 | votes = "" 217 | for reaction in s_message.reactions: 218 | votes += f"{reaction.emoji} **- {reaction.count - 1 if reaction.me else reaction.count}**\n" 219 | if votes: 220 | embed.add_field(name="Votes", value=votes, inline=False) 221 | await s_message.edit(embed=embed) 222 | await s_message.clear_reactions() 223 | 224 | @commands.command() 225 | @checks.has_permissions(PermissionLevel.ADMIN) 226 | async def setsuggestchannel(self, ctx, *, channel: discord.TextChannel): 227 | """ 228 | Set the channel where suggestions go. 229 | 230 | **Usage**: 231 | [p]setsuggestchannel #suggestions 232 | [p]ssc suggestions 233 | [p]ssc 515085600047628288 234 | """ 235 | await self.coll.find_one_and_update( 236 | {"_id": "config"}, 237 | {"$set": {"suggestion-channel": {"channel": str(channel.id)}}}, 238 | upsert=True, 239 | ) 240 | embed = discord.Embed( 241 | title=f"Set suggestion channel to #{channel}.", color=0x4DFF73 242 | ) 243 | embed.set_author(name="Success!") 244 | embed.set_footer(text="Task succeeded successfully.") 245 | await ctx.send(embed=embed) 246 | 247 | @commands.command() 248 | @checks.has_permissions(PermissionLevel.ADMIN) 249 | async def suggestchannel(self, ctx): 250 | """Displays the suggestion channel.""" 251 | config = await self.coll.find_one({"_id": "config"}) 252 | suggestion_channel = self.bot.get_channel( 253 | int(config["suggestion-channel"]["channel"]) 254 | ) 255 | embed = discord.Embed( 256 | title=f"The suggestion channel is: #{suggestion_channel}", 257 | description="To change it, use [p]setsuggestchannel.", 258 | color=0x4DFF73, 259 | ) 260 | await ctx.send(embed=embed) 261 | 262 | @commands.command() 263 | @checks.has_permissions(PermissionLevel.ADMIN) 264 | async def setemojis(self, ctx, *emojis: discord.Emoji): 265 | """ 266 | Set emojis to react to each suggestion with. 267 | 268 | **Usage**: 269 | [p]setemojis \N{WHITE HEAVY CHECK MARK} \N{CROSS MARK} 270 | [p]se (custom emojis) 271 | """ 272 | await self.coll.find_one_and_update( 273 | {"_id": "config"}, 274 | {"$set": {"reaction-emojis": {"emojis": [i.id for i in emojis]}}}, 275 | upsert=True, 276 | ) 277 | embed = discord.Embed(title=f"Set emojis.", color=0x4DFF73) 278 | embed.set_author(name="Success!") 279 | embed.set_footer(text="Task succeeded successfully.") 280 | await ctx.send(embed=embed) 281 | 282 | @checks.has_permissions(PermissionLevel.MOD) 283 | @commands.group(invoke_without_command=True) 284 | async def suggestmod(self, ctx: commands.Context): 285 | """Let's you block and unblock people from using the suggest command.""" 286 | await ctx.send_help(ctx.command) 287 | 288 | @suggestmod.command(aliases=["ban"]) 289 | @checks.has_permissions(PermissionLevel.MOD) 290 | async def block(self, ctx, user: discord.User, *, reason="Reason not specified."): 291 | """ 292 | Block a user from using the suggest command. 293 | 294 | **Examples:** 295 | [p]suggestmod block @RealCyGuy for abuse! 296 | [p]suggestmod ban 543225108135673877 `cause he's the same person!!! 297 | """ 298 | if str(user.id) in self.banlist: 299 | embed = discord.Embed( 300 | colour=self.bot.error_color, 301 | title=f"{user.name}#{user.discriminator} is already blocked.", 302 | description=f"Reason: {self.banlist[str(user.id)]}", 303 | ) 304 | else: 305 | self.banlist[str(user.id)] = reason 306 | embed = discord.Embed( 307 | colour=self.bot.main_color, 308 | title=f"{user.name}#{user.discriminator} is now blocked.", 309 | description=f"Reason: {reason}", 310 | ) 311 | 312 | await self._update_mod_db() 313 | await ctx.send(embed=embed) 314 | 315 | @suggestmod.command(aliases=["unban"]) 316 | @checks.has_permissions(PermissionLevel.MOD) 317 | async def unblock(self, ctx, user: discord.User): 318 | """ 319 | Unblock a user from using the suggest command. 320 | 321 | **Examples:** 322 | [p]suggestmod unblock @RealCyGuy 323 | [p]suggestmod unban 543225108135673877 324 | """ 325 | if str(user.id) not in self.banlist: 326 | embed = discord.Embed( 327 | colour=self.bot.error_color, 328 | title=f"{user.name}#{user.discriminator} is not blocked.", 329 | description=f"Reason: {self.banlist[str(user.id)]}", 330 | ) 331 | else: 332 | self.banlist.pop(str(user.id)) 333 | embed = discord.Embed( 334 | colour=self.bot.main_color, 335 | title=f"{user.name}#{user.discriminator} is now unblocked.", 336 | ) 337 | 338 | await self._update_mod_db() 339 | await ctx.send(embed=embed) 340 | 341 | 342 | async def setup(bot): 343 | await bot.add_cog(Suggest(bot)) 344 | -------------------------------------------------------------------------------- /verification/captcha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RealCyGuy/modmail-plugins/b381bd1eeaeacb591d2313cf7a1744f00f5baf53/verification/captcha.png -------------------------------------------------------------------------------- /verification/requirements.txt: -------------------------------------------------------------------------------- 1 | captcha -------------------------------------------------------------------------------- /verification/verification.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from discord.ext import commands 3 | 4 | import asyncio 5 | 6 | from captcha.image import ImageCaptcha 7 | 8 | import string 9 | import random 10 | import os 11 | 12 | from core import checks 13 | from core.models import PermissionLevel 14 | 15 | 16 | class CaptchaVerification(commands.Cog): 17 | """Add captcha verification to your server!""" 18 | 19 | def __init__(self, bot): 20 | self.bot = bot 21 | self.db = bot.plugin_db.get_partition(self) 22 | 23 | self.role = dict() 24 | self.length = int() 25 | self.captchas = dict() 26 | self.casesensitive = bool() 27 | 28 | asyncio.create_task(self._set_val()) 29 | 30 | self.true = ["t", "true", "yes", "y", "on"] 31 | self.false = ["f", "false", "no", "n", "off"] 32 | 33 | async def _update_db(self): 34 | await self.db.find_one_and_update( 35 | {"_id": "config"}, 36 | { 37 | "$set": { 38 | "roles": self.role, 39 | "length": self.length, 40 | "case-sensitive": self.casesensitive, 41 | } 42 | }, 43 | upsert=True, 44 | ) 45 | await self.db.find_one_and_update( 46 | {"_id": "captchas"}, 47 | {"$set": {"current_captchas": self.captchas}}, 48 | upsert=True, 49 | ) 50 | 51 | async def _set_val(self): 52 | config = await self.db.find_one({"_id": "config"}) 53 | captchas = await self.db.find_one({"_id": "captchas"}) 54 | 55 | if config is None: 56 | await self.db.find_one_and_update( 57 | {"_id": "config"}, 58 | {"$set": {"roles": dict(), "length": 7, "case-sensitive": False,}}, 59 | upsert=True, 60 | ) 61 | config = await self.db.find_one({"_id": "config"}) 62 | 63 | if captchas is None: 64 | await self.db.find_one_and_update( 65 | {"_id": "captchas"}, {"$set": {"current_captchas": dict()}}, upsert=True 66 | ) 67 | 68 | captchas = await self.db.find_one({"_id": "captchas"}) 69 | 70 | self.role = config.get("roles", dict()) 71 | self.length = config.get("length", 7) 72 | self.casesensitive = config.get("case-sensitive", False) 73 | 74 | self.captchas = captchas["current_captchas"] 75 | 76 | @commands.command() 77 | async def captcha(self, ctx, code=None): 78 | def replace_similar(text): 79 | return text.replace("0", "o") 80 | 81 | if str(ctx.guild.id) in self.role: 82 | if code is None: 83 | code = replace_similar( 84 | "".join( 85 | random.choices( 86 | string.ascii_letters + string.digits, k=self.length 87 | ) 88 | ) 89 | ) 90 | image = ImageCaptcha() 91 | image.write( 92 | code, os.path.join(os.path.dirname(__file__), "captcha.png") 93 | ) 94 | embed = discord.Embed( 95 | colour=self.bot.main_color, 96 | title="Your captcha, good sir (or ma'am).", 97 | ) 98 | embed.set_footer( 99 | text=f"Use {self.bot.prefix}captcha in a channel (not this DM) to solve it." 100 | ) 101 | embed.set_image(url="attachment://captcha.png") 102 | file = discord.File( 103 | os.path.join(os.path.dirname(__file__), "captcha.png"), 104 | filename="captcha.png", 105 | ) 106 | await ctx.author.send(file=file, embed=embed) 107 | await ctx.send( 108 | f"{ctx.author.mention}, sent you a DM containing the CAPTCHA!" 109 | ) 110 | self.captchas[str(ctx.author.id)] = code 111 | elif str(ctx.author.id) in self.captchas: 112 | if replace_similar(code) == self.captchas[str(ctx.author.id)]: 113 | solved = True 114 | elif ( 115 | not self.casesensitive 116 | and replace_similar(code.lower()) 117 | == self.captchas[str(ctx.author.id)].lower() 118 | ): 119 | solved = True 120 | else: 121 | await ctx.send( 122 | embed=discord.Embed( 123 | title=f"That is incorrect. Please try again. Use `{self.bot.prefix}captcha`", 124 | description=f"It is {None if self.casesensitive else 'not '}case-sensitive.", 125 | colour=self.bot.error_color, 126 | ) 127 | ) 128 | solved = False 129 | if solved: 130 | try: 131 | role = discord.utils.get( 132 | ctx.guild.roles, id=int(self.role[str(ctx.guild.id)]) 133 | ) 134 | await ctx.author.add_roles(role) 135 | await ctx.send(f"You got the role: `{role.name}`") 136 | except discord.Forbidden: 137 | await ctx.send( 138 | "I don't have the permissions to give you the role." 139 | ) 140 | except: 141 | await ctx.send("I couldn't give you the role.") 142 | self.captchas.pop(str(ctx.author.id)) 143 | 144 | else: 145 | await ctx.send( 146 | embed=discord.Embed( 147 | colour=self.bot.error_color, 148 | title="You are not doing a captcha.", 149 | ) 150 | ) 151 | else: 152 | await ctx.send( 153 | embed=discord.Embed( 154 | colour=self.bot.error_color, title="No role set for doing captcha." 155 | ) 156 | ) 157 | 158 | @checks.has_permissions(PermissionLevel.ADMIN) 159 | @commands.group(invoke_without_command=True) 160 | async def captchaconfig(self, ctx): 161 | """ 162 | Config your captcha! 163 | 164 | To view your settings, use [p]captchaconfig. 165 | 166 | To edit, use [p]captchaconfig 167 | """ 168 | role = str(self.role.get(str(ctx.guild.id), "No role specified.")) 169 | 170 | embed = discord.Embed(colour=self.bot.main_color) 171 | embed.set_author( 172 | name="Captcha Configurations:", icon_url=self.bot.user.avatar_url 173 | ) 174 | embed.add_field(name="Role", value=f"`{role}`", inline=False) 175 | embed.add_field(name="Code Length", value=f"`{self.length}`", inline=False) 176 | embed.add_field( 177 | name="Case Sensitive", value=f"`{self.casesensitive}`", inline=False 178 | ) 179 | embed.set_footer( 180 | text=f"To change use {self.bot.prefix}captchaconfig . Use {self.bot.prefix}help captchaconfig for the list of things you want to change." 181 | ) 182 | await ctx.send(embed=embed) 183 | 184 | @checks.has_permissions(PermissionLevel.ADMIN) 185 | @captchaconfig.command() 186 | async def role(self, ctx, role: discord.Role): 187 | """ 188 | Set the role you get when you complete the captcha. 189 | 190 | **Usage**: 191 | [p]captchaconfig role @Done 192 | [p]captchaconfig role 682773117680091148 193 | [p]captchaconfig role Verified Member 194 | """ 195 | self.role[str(ctx.guild.id)] = role.id 196 | await self._update_db() 197 | await ctx.send("Ok.") 198 | 199 | @checks.has_permissions(PermissionLevel.ADMIN) 200 | @captchaconfig.command() 201 | async def length(self, ctx, length=7): 202 | """ 203 | Set the length of the randomly generated code. 204 | 205 | **Usage**: 206 | [p]captchaconfig length 2 207 | [p]captchaconfig length 15 208 | 209 | It has to be between 1 and 20 210 | """ 211 | if length > 0 and length < 21: 212 | self.length = length 213 | await self._update_db() 214 | await ctx.send("Ok.") 215 | else: 216 | await ctx.send("Too big or too small. 1-20 please.") 217 | 218 | @checks.has_permissions(PermissionLevel.ADMIN) 219 | @captchaconfig.command(aliases=["cs"]) 220 | async def casesensitive(self, ctx, trueorfalse): 221 | """ 222 | Set if codes are case sensitive. 223 | 224 | **Usage**: 225 | [p]captchaconfig cs true 226 | [p]captchaconfig casesensitive fAlse 227 | 228 | **True**: 229 | t, true, y, yes, on 230 | 231 | **False**: 232 | f, false, n, no, off 233 | 234 | True or falses are not case sensitive. 235 | """ 236 | if trueorfalse.lower() in self.true: 237 | self.casesensitive = True 238 | await self._update_db() 239 | await ctx.send(f"Case sensitive is now set to `{self.casesensitive}`") 240 | elif trueorfalse.lower() in self.false: 241 | self.casesensitive = False 242 | await self._update_db() 243 | await ctx.send(f"Case sensitive is now set to `{self.casesensitive}`") 244 | else: 245 | await ctx.send("I don't understand.") 246 | 247 | 248 | async def setup(bot): 249 | await bot.add_cog(CaptchaVerification(bot)) 250 | -------------------------------------------------------------------------------- /webserver/webserver.py: -------------------------------------------------------------------------------- 1 | from aiohttp import web 2 | from discord.ext import commands 3 | 4 | 5 | class WebServer(commands.Cog): 6 | """ 7 | Runs a simple webserver on port 8080 for stuff like health checks. 8 | """ 9 | 10 | def __init__(self, bot): 11 | self.bot = bot 12 | 13 | @commands.Cog.listener() 14 | async def on_plugins_ready(self): 15 | async def ping(request): 16 | return web.Response(text="pong") 17 | 18 | app = web.Application() 19 | app.add_routes([web.get("/", ping)]) 20 | 21 | runner = web.AppRunner(app) 22 | await runner.setup() 23 | site = web.TCPSite(runner, port=8080) 24 | await site.start() 25 | 26 | 27 | async def setup(bot): 28 | await bot.add_cog(WebServer(bot)) 29 | --------------------------------------------------------------------------------