commit be4ac63db7cf59c4003e99a6875363bb3e525bc2
Author: Jiri Karlik
Date: Sat Aug 10 09:10:28 2024 +0200
Transfer to Gitea
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2483976
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+.idea/
+__pycache__/
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..f626319
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 Karlik Jiri
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..97cfe53
--- /dev/null
+++ b/README.md
@@ -0,0 +1,211 @@
+
+
+
+
+
+
+[![Contributors][contributors-shield]][contributors-url]
+[![Forks][forks-shield]][forks-url]
+[![Stargazers][stars-shield]][stars-url]
+[![Issues][issues-shield]][issues-url]
+[![MIT License][license-shield]][license-url]
+[![LinkedIn][linkedin-shield]][linkedin-url]
+
+
+
+
+
+
+
+
+
+
+
RandBot
+ PROJECT DISCONTINUED due to lack of interest
+
+
+ Discord bot randomizer. Users can create a list that is stored in MongoDB and the bot can select a random item from this list.
+ ·
+ Report Bug
+ ·
+ Request Feature
+
+
+
+
+
+
+
+ Table of Contents
+
+ -
+ About The Project
+
+
+ -
+ Getting Started
+
+
+ - Bot Commands
+ - Roadmap
+ - Contributing
+ - License
+ - Contact
+
+
+
+
+
+
+## About The Project
+
+Discord bot created in Python using [discord.py](https://discordpy.readthedocs.io/).
+The bot can create custom lists using the ?list command and stores those lists in MongoDB.
+Those lists can also be deleted using the command ?delete.
+Bot returns 1 random item from the selected list when command ?random is used.
+
+The project is now in the beta version. This is the first working version that I am using on my Discord server to randomize drop location in Call of Duty: Warzone.
+It is currently working, but error handling should be updated and new features can be added. This was a fun project to use on my Discord, but I have decided to share it with others. I am currently hosting the bot as well, so you can also invite the bot to your server instead of running it on your own.
+
+
+### Built With
+
+* [Python](https://www.python.org/)
+* [MongoDB](https://www.mongodb.com/)
+
+
+
+
+## Getting Started
+
+
+Download the repository and create the MongoDB Atlas database. More details in the Installation section.
+
+### Prerequisites
+
+- [Python 3](https://www.python.org/)
+- Following Python modules are needed. I recommend installing them with [pip](https://pip.pypa.io/en/stable/installing/):
+ - [discord](https://discordpy.readthedocs.io/)
+ - [pymongo](https://pymongo.readthedocs.io/)
+ - [asyncio](https://docs.python.org/3/library/asyncio.html)
+ - [dnspython](https://www.dnspython.org/)
+- [MongoDB Atlas](https://www.mongodb.com/cloud/atlas) - There is free database option. I am currently using following DB structure "bot.lists", which is also part of the code in commands.py. It can be changed there:
+ ```sh
+ db = clientDB.bot
+ collection = db.lists
+ ```
+
+
+### Installation
+
+1. Clone the repo
+ ```sh
+ git clone https://github.com/karlji/RandBot-Discord-Randomizer.git
+ ```
+2. Create Discord bot on [Discord Developer Portal](https://discord.com/developers/docs/intro) & store the bot token to tokens.py
+ -
+4. Create [MongoDB Atlas cluster](https://www.mongodb.com/cloud/atlas) with DB structure "bot.lists", Document structure visible below, add IP adress of your bot machine, copy "connection string" to tokens.py
+ -
+ -
+5. Run randbot.py
+6. Create OAuth2 link on [Discord Developer Portal](https://discord.com/developers/). Options "bot" and "send messages" should be enough. This link can be used to invite bot to Discord servers.
+ -
+
+
+
+## Commands
+
+Following commands are available:
+
+- ?commands
+ - Lists all available commands.
+- ?list {ListName}
+ - Creates new list.
+- ?shuffle {ListName}
+ - Randomly selects one item from list.
+- ?delete {ListName}
+ - Deletes existing list.
+- ?showlists
+ - Shows existing lists.
+- ?yesno
+ - Randomly answers Yes/No.
+- ?8ball
+ - Answers like The Magic 8 Ball.
+
+
+
+## Roadmap
+
+See the [open issues](https://github.com/github_username/repo_name/issues) for a list of proposed features (and known issues).
+
+
+
+
+## Contributing
+
+Contributions are what makes the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
+
+1. Fork the Project
+2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
+3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
+4. Push to the Branch (`git push origin feature/AmazingFeature`)
+5. Open a Pull Request
+
+
+
+
+## License
+
+Distributed under the MIT License. See `LICENSE` for more information.
+
+
+
+
+## Contact
+
+Jiri Karlik- [Linkedin](https://www.linkedin.com/in/jiri-karlik/)
+
+Project Link: [https://github.com/karlji/RandBot-Discord-Randomizer](https://github.com/karlji/RandBot-Discord-Randomizer)
+
+
+
+
+
+
+
+
+
+
+[contributors-shield]: https://img.shields.io/github/contributors/karlji/RandBot-Discord-Randomizer.svg?style=for-the-badge
+[contributors-url]: https://github.com/karlji/RandBot-Discord-Randomizer/graphs/contributors
+[forks-shield]: https://img.shields.io/github/forks/karlji/RandBot-Discord-Randomizer.svg?style=for-the-badge
+[forks-url]: https://github.com/karlji/RandBot-Discord-Randomizer/network/members
+[stars-shield]: https://img.shields.io/github/stars/karlji/repo.svg?style=for-the-badge
+[stars-url]: https://github.com/karlji/RandBot-Discord-Randomizer/stargazers
+[issues-shield]: https://img.shields.io/github/issues/karlji/RandBot-Discord-Randomizer.svg?style=for-the-badge
+[issues-url]: https://github.com/karlji/RandBot-Discord-Randomizer/issues
+[license-shield]: https://img.shields.io/github/license/karlji/RandBot-Discord-Randomizer.svg?style=for-the-badge
+[license-url]: https://github.com/karlji/RandBot-Discord-Randomizer/blob/master/LICENSE.txt
+[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555
+[linkedin-url]: https://www.linkedin.com/in/jiri-karlik/
diff --git a/commands.py b/commands.py
new file mode 100644
index 0000000..7ee612d
--- /dev/null
+++ b/commands.py
@@ -0,0 +1,157 @@
+import random
+import json
+import asyncio
+import pymongo
+import messages as mes
+import tokens as tok
+import time
+
+clientDB = pymongo.MongoClient(tok.mongo_token)
+db = clientDB.bot
+
+
+# Base class for other exceptions
+class Error(Exception):
+ pass
+
+
+# Raised when the list format is wrong
+class ListFormatError(Error):
+ pass
+
+
+class ListExistsError(Error):
+ pass
+
+
+async def list_command(client, message):
+ server_name = message.guild.name
+ collection = db[server_name]
+ list_name = message.content.replace('?list ', '')
+ title = "Creating list: " + list_name
+ full_user = message.author.name + "#" + message.author.discriminator
+ await message.channel.send(embed=mes.list_message(title))
+
+ try:
+ def check(m):
+ # checking if awaited message is from the same user that used the command
+ if full_user == m.author.name + "#" + m.author.discriminator:
+ # checking if user input contains ; ... else returning Format error
+ if m.content.find(";") != -1:
+ return True
+ else:
+ raise ListFormatError
+ else:
+ return False
+
+ if collection.find_one({"User": full_user, "List_Name": list_name}, {"List": 1, "_id": False}) is None:
+ message = await client.wait_for('message', timeout=60.0, check=check)
+ else:
+ raise ListExistsError
+ # Format error + timeout error
+ except asyncio.TimeoutError:
+ await message.channel.send(embed=mes.timeout_message())
+ except ListFormatError:
+ await message.channel.send(embed=mes.format_error_message())
+ except ListExistsError:
+ await message.channel.send(embed=mes.list_exists_error_message())
+
+ # if no errors occurred, create new list
+ else:
+ now = int(time.time())
+ collection.insert_one(
+ {"User": full_user, "List_Name": list_name, "List": message.content, "Timestamp": now})
+ title = list_name + " created!"
+ await message.channel.send(embed=mes.list_created_message(title))
+ return
+
+
+async def random_command(message):
+ list_name = message.content.replace('?random ', '')
+ server_name = message.guild.name
+ full_user = message.author.name + "#" + message.author.discriminator
+ # calling randomize function to get random item from list
+ item = randomize(server_name, full_user, list_name)
+ await message.channel.send(embed=mes.random_message(item))
+ return
+
+
+async def delete_command(message):
+ list_name = message.content.replace('?delete ', '')
+ server_name = message.guild.name
+ collection = db[server_name]
+ full_user = message.author.name + "#" + message.author.discriminator
+ collection.delete_one({"User": full_user, "List_Name": list_name})
+ title = list_name + " deleted!"
+ await message.channel.send(embed=mes.delete_message(title))
+ return
+
+
+async def commands_command(message):
+ await message.channel.send(embed=mes.commands_message())
+ return
+
+
+# retrieving random item from list
+def randomize(server_name, full_user, list_name):
+ collection = db[server_name]
+ # query based on listname & username
+ output = collection.find_one({"User": full_user, "List_Name": list_name}, {"List": 1, "_id": False})
+ # adding timestamp of the latest use to the list
+ now = int(time.time())
+ timestamp = {"$set": {"Timestamp": now}}
+ collection.update_one(output, timestamp)
+ # formatting the query output
+ output = json.dumps(output)
+ output = output.replace('{"List": "', '')
+ output = output.replace('"}', '')
+ output = output.split(";")
+ # selecting random item from the output
+ output = random.choice(tuple(output))
+ # check for when query returns null
+ if output == "null":
+ output = "List not found!"
+ return output
+
+
+async def eightball_command(message):
+ answers = ("It is certain.", "It is decidedly so.", "Without a doubt.", "Yes – definitely.", "You may rely on it.",
+ "As I see it, yes.", "Most likely.", "Outlook good.", "Yes.", "Signs point to yes.",
+ "Reply hazy, try again.", "Ask again later.", "Better not tell you now.", "Cannot predict now.",
+ "Concentrate and ask again.", "Don't count on it.", "My reply is no.", "My sources say no.",
+ "Outlook not so good.", "Very doubtful.")
+ item = random.choice(answers)
+ await message.channel.send(embed=mes.random_message(item))
+ return
+
+
+async def yesno_command(message):
+ answers = ("yes", "no")
+ item = random.choice(answers)
+ await message.channel.send(embed=mes.random_message(item))
+ return
+
+
+async def clean():
+ col_list = db.list_collection_names()
+ now = int(time.time())
+ difference = now - 2592000
+
+ # Loops through all collections and deletes those that are older than 30 days from now
+ for i in range(len(col_list)):
+ col = col_list[i]
+ collection = db[col]
+ myquery = {"Timestamp": {"$lt": difference}}
+ collection.delete_many(myquery)
+ return
+
+
+async def print_lists(message):
+ server_name = message.guild.name
+ full_user = message.author.name + "#" + message.author.discriminator
+ collection = db[server_name]
+ query = collection.find({"User": full_user}, {"List_Name": 1, "_id": False})
+ array = list(query)
+ length = len(array)
+ await message.channel.send(embed=mes.print_lists_message(array,length))
+ return
diff --git a/images/discord_token.png b/images/discord_token.png
new file mode 100644
index 0000000..8a4e28c
Binary files /dev/null and b/images/discord_token.png differ
diff --git a/images/logo.png b/images/logo.png
new file mode 100644
index 0000000..c5f369e
Binary files /dev/null and b/images/logo.png differ
diff --git a/images/mongo_document.png b/images/mongo_document.png
new file mode 100644
index 0000000..1ccbd40
Binary files /dev/null and b/images/mongo_document.png differ
diff --git a/images/mongo_token.png b/images/mongo_token.png
new file mode 100644
index 0000000..f6df8cc
Binary files /dev/null and b/images/mongo_token.png differ
diff --git a/images/oauth.png b/images/oauth.png
new file mode 100644
index 0000000..568da4e
Binary files /dev/null and b/images/oauth.png differ
diff --git a/messages.py b/messages.py
new file mode 100644
index 0000000..22570e5
--- /dev/null
+++ b/messages.py
@@ -0,0 +1,142 @@
+import discord
+import json
+
+# List of embeded messages to clean up the code
+
+def list_message(title):
+ embed = discord.Embed(title=title,
+ description="Please enter your list items separated by ; ",
+ color=0xFF5733)
+ embed.add_field(name="Example", value="Item1;Item2;Item3;Item4", inline=False)
+ embed.add_field(name="Support this project",
+ value="[Donate](https://www.paypal.com/donate?hosted_button_id=QY9QSBC63TL34)",
+ inline=False)
+ return embed
+
+
+def timeout_message():
+ embed = discord.Embed(title="No list provided within timeout!",
+ description="There is 60s timeout. ",
+ color=0xFF5733)
+ embed.add_field(name="Example", value="Please start again with ?list command.", inline=False)
+ return embed
+
+
+def format_error_message():
+ embed = discord.Embed(title="Format Error!",
+ description="Use ; separator between items! ",
+ color=0xFF5733)
+ embed.add_field(name="Example", value="Please start again with ?list command.", inline=False)
+ return embed
+
+
+def list_exists_error_message():
+ embed = discord.Embed(title="List already exists!",
+ description="Please use unique list name.",
+ color=0xFF5733)
+ embed.add_field(name="Example", value="Please start again with ?list command.", inline=False)
+ return embed
+
+
+def guild_join_message():
+ embed = discord.Embed(title="Thanks for inviting me:",
+ description="Following commands are available:",
+ color=0xFF5733)
+
+ embed.add_field(name="?list {ListName}",
+ value="Creates new list.",
+ inline=False)
+
+ embed.add_field(name="?random {ListName}",
+ value="Randomly selects one item from list.",
+ inline=False)
+
+ embed.add_field(name="?delete {ListName}",
+ value="Deletes existing list.",
+ inline=False)
+
+ embed.add_field(name="?commands",
+ value="Lists all available commands.",
+ inline=False)
+ return embed
+
+
+def list_created_message(title):
+ embed = discord.Embed(title=title,
+ description="New list created!",
+ color=0xFF5733)
+ embed.add_field(name="Support this project",
+ value="[Donate](https://www.paypal.com/donate?hosted_button_id=QY9QSBC63TL34)",
+ inline=False)
+ return embed
+
+
+def random_message(item):
+ embed = discord.Embed(title=item,
+ description="[Support this project](https://www.paypal.com/donate?hosted_button_id=QY9QSBC63TL34)",
+ color=0xFF5733)
+ return embed
+
+
+def delete_message(title):
+ embed = discord.Embed(title=title,
+ description="List deleted!",
+ color=0xFF5733)
+ embed.add_field(name="Support this project",
+ value="[Donate](https://www.paypal.com/donate?hosted_button_id=QY9QSBC63TL34)",
+ inline=False)
+ return embed
+
+
+def commands_message():
+ embed = discord.Embed(title="Commands:",
+ description="Following commands are available:",
+ color=0xFF5733)
+
+ embed.add_field(name="?commands",
+ value="Lists all available commands.",
+ inline=False)
+
+ embed.add_field(name="?delete {ListName}",
+ value="Deletes existing list.",
+ inline=False)
+
+ embed.add_field(name="?list {ListName}",
+ value="Creates new list.",
+ inline=False)
+
+ embed.add_field(name="?random {ListName}",
+ value="Randomly selects one item from the list.",
+ inline=False)
+
+ embed.add_field(name="?showlists",
+ value="Prints all available lists for the user.",
+ inline=False)
+
+ embed.add_field(name="?yesno",
+ value="Gives Yes or No answer.",
+ inline=False)
+
+ embed.add_field(name="?8ball",
+ value="Gives random 8ball answer.",
+ inline=False)
+
+ return embed
+
+
+def print_lists_message(array, length):
+ embed = discord.Embed(title="Show all lists",
+ description="Following lists are availible to you:",
+ color=0xFF5733)
+ for i in range(length):
+ item = array[i]
+ item = json.dumps(item)
+ item = item.replace('{"List_Name": "', '')
+ item = item.replace('"}', '')
+ embed.add_field(name=item,
+ value="\u200b",
+ inline=False)
+ embed.add_field(name="Support this project",
+ value="[Donate](https://www.paypal.com/donate?hosted_button_id=QY9QSBC63TL34)",
+ inline=False)
+ return embed
diff --git a/randbot.py b/randbot.py
new file mode 100644
index 0000000..18c406d
--- /dev/null
+++ b/randbot.py
@@ -0,0 +1,54 @@
+import discord
+import commands as com
+import messages as mes
+import tokens as tok
+from discord.ext import tasks
+import datetime
+
+client = discord.Client()
+
+
+# Information Embed message when bot joins the server for the first time.
+@client.event
+async def on_guild_join(guild):
+ await guild.text_channels[0].send(embed=mes.guild_join_message())
+
+
+# Information to console when bot successfully logs in the server.
+@client.event
+async def on_ready():
+ print('We have logged in as {0.user}'.format(client))
+
+
+# Reactions to message commands.
+@client.event
+async def on_message(message):
+ if message.author == client.user:
+ return
+ elif message.content.startswith('?random'):
+ await com.random_command(message)
+ elif message.content.startswith('?8ball'):
+ await com.eightball_command(message)
+ elif message.content.startswith('?yesno'):
+ await com.yesno_command(message)
+ elif message.content.startswith('?list'):
+ await com.list_command(client,message)
+ elif message.content.startswith('?showlists'):
+ await com.print_lists(message)
+ elif message.content.startswith('?delete'):
+ await com.delete_command(message)
+ elif message.content.startswith('?commands'):
+ await com.commands_command(message)
+
+
+# daily DB clean loop
+@tasks.loop(hours=24)
+async def cleandb():
+ await com.clean()
+
+ print("DB cleaned " + datetime.datetime.utcnow().strftime('%B %d %Y - %H:%M:%S'))
+
+cleandb.start()
+
+# Connects the client using Discord bot token
+client.run(tok.discord_token)
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..a797332
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,4 @@
+discord
+pymongo
+asyncio
+dnspython
\ No newline at end of file
diff --git a/tokens.py b/tokens.py
new file mode 100644
index 0000000..2ace5fb
--- /dev/null
+++ b/tokens.py
@@ -0,0 +1,2 @@
+mongo_token = ''
+discord_token = ''