├── role.txt ├── token.txt ├── negatives.txt ├── keywords.txt ├── channels.txt ├── log.py ├── LICENSE ├── README.md ├── .gitignore └── main.py /role.txt: -------------------------------------------------------------------------------- 1 | Sushi Gang -------------------------------------------------------------------------------- /token.txt: -------------------------------------------------------------------------------- 1 | YOUR_DISCORD_TOKEN_HERE -------------------------------------------------------------------------------- /negatives.txt: -------------------------------------------------------------------------------- 1 | negative 2 | keywords 3 | here -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | a 2 | e 3 | i 4 | o 5 | u 6 | yeezy+350 7 | jordan+1 -------------------------------------------------------------------------------- /channels.txt: -------------------------------------------------------------------------------- 1 | shopify-unfiltered 2 | supreme-us 3 | supreme-eu 4 | supreme-jp -------------------------------------------------------------------------------- /log.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | def log(tag, text): 4 | # Info tag 5 | if(tag == 'i'): 6 | print("[" + str(datetime.datetime.now()) + " - INFO] " + text) 7 | # Error tag 8 | elif(tag == 'e'): 9 | print("[" + str(datetime.datetime.now()) + " - ERROR] " + text) 10 | # Success tag 11 | elif(tag == 's'): 12 | print("[" + str(datetime.datetime.now()) + " - SUCCESS] " + text) 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Niveen Jegatheeswaran 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 | # discord-keyword-alerts 2 | A Discord bot to alert members with a role when certain keywords are matched. 3 | 4 | This bot mentions a specified role whenever a keyword match is found. The bot only works for embed messages and not normal user messages, as it's main purpose is to support Discord based cook groups. 5 | 6 | ### Usage: 7 | Keyword matches are determined based on a specified list of keywords and negative keywords. If any of the negative keywords match, the message will be ignored. You can set up your keywords in "keywords.txt" and negative keywords in "negatives.txt". If you want to use multiple words (i.e. define a keyword set), separate words with '+' in between (ex. air+jordan+1). 8 | 9 | You must create a Discord bot and get its token, which should be put in "token.txt". A full guide on how to do this, with images, can be found [here](https://github.com/Chikachi/DiscordIntegration/wiki/How-to-get-a-token-and-channel-ID-for-Discord). 10 | 11 | You must set up which channels you want to monitor for the keywords. List them 1 per line (example included in the .txt file). 12 | 13 | Lastly, you must define which role to mention when a keyword match is detected. You can do this by simply entering the role you want to mention in "role.txt". 14 | 15 | All settings can be changed on the fly so you don't have to restart the script if you want to change something. 16 | 17 | ### Screenshot: 18 | ![NERYS Example](https://i.gyazo.com/4a08077d54abfc90326f7639695955b6.png) 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import discord 2 | from log import log as log 3 | 4 | def read_from_txt(path): 5 | # Initialize variables 6 | raw_lines = [] 7 | lines = [] 8 | 9 | # Load data from the txt file 10 | try: 11 | f = open(path, "r") 12 | raw_lines = f.readlines() 13 | f.close() 14 | 15 | # Raise an error if the file couldn't be found 16 | except: 17 | log('e', "Couldn't locate <" + path + ">.") 18 | return None 19 | 20 | # Parse the data 21 | for line in raw_lines: 22 | lines.append(line.strip("\n")) 23 | 24 | # Return the data 25 | return lines 26 | 27 | # Create the Discord client 28 | client = discord.Client() 29 | 30 | # Read which channels to monitor 31 | active_channels = read_from_txt("channels.txt") 32 | role_to_notify = read_from_txt("role.txt")[0] 33 | 34 | def keyword_check(text): 35 | keywords = read_from_txt("keywords.txt") 36 | negatives = read_from_txt("negatives.txt") 37 | 38 | for keyword_set in keywords: 39 | good = False 40 | matches = 0 41 | total = len(keyword_set.split('+')) 42 | for keyword in keyword_set.split('+'): 43 | if(keyword.lower() in text.lower()): 44 | matches += 1 45 | if(matches == total): 46 | good = True 47 | break 48 | 49 | for negative in negatives: 50 | if(negative.lower() in text.lower()): 51 | good = False 52 | break 53 | 54 | if(good): 55 | return (True, keyword_set) 56 | else: 57 | return (False, None) 58 | 59 | @client.event 60 | async def on_ready(): 61 | log('s', "Kolaveri vanthutaal >:)") 62 | 63 | @client.event 64 | async def on_message(message): 65 | # Ignore messages made by the bot 66 | if(message.author == client.user): 67 | return 68 | 69 | # Ignore messages not in one of the specified channels 70 | if(message.channel.name not in active_channels): 71 | return 72 | 73 | # Parse embed 74 | total_text = "" 75 | try: 76 | total_text += message.embeds[0]["description"] 77 | # Issue: Non embed messages will raise an error. Solution: Ignore. 78 | except: 79 | pass 80 | 81 | try: 82 | total_text += message.embeds[0]["title"] 83 | # Issue: Non embed messages will raise an error. Solution: Ignore. 84 | except: 85 | pass 86 | 87 | # If the keyword is found 88 | keyword_match = keyword_check(total_text) 89 | if(keyword_match[0]): 90 | role = discord.utils.get(message.server.roles, name=read_from_txt("role.txt")[0]) 91 | # Create an embed to notify memvers 92 | em = discord.Embed(description="Keyword match detected.\n" + role.mention, 93 | color=11177686) 94 | log('i', "Detected keyword match: " + keyword_match[1]) 95 | 96 | em.add_field(name="Keyword Matched", value=keyword_match[1]) 97 | 98 | em.set_footer(text=message.server.name + " x @SharangaIO | Keyword Notify", icon_url="https://i.imgur.com/eN3OhSG.jpg") 99 | 100 | # Send the notification 101 | await client.send_message(message.channel, embed=em) 102 | 103 | client.run(read_from_txt("token.txt")[0]) 104 | --------------------------------------------------------------------------------