Transfer to Gitea

This commit is contained in:
Jiri Karlik 2024-08-10 09:10:28 +02:00
commit be4ac63db7
13 changed files with 593 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.idea/
__pycache__/

21
LICENSE.txt Normal file
View File

@ -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.

211
README.md Normal file
View File

@ -0,0 +1,211 @@
<!--
*** Thanks for checking out the Best-README-Template. If you have a suggestion
*** that would make this better, please fork the repo and create a pull request
*** or simply open an issue with the tag "enhancement".
*** Thanks again! Now go create something AMAZING! :D
***
***
***
*** To avoid retyping too much info. Do a search and replace for the following:
*** github_username, repo_name, twitter_handle, email, project_title, project_description
-->
<!-- PROJECT SHIELDS -->
<!--
*** I'm using markdown "reference style" links for readability.
*** Reference links are enclosed in brackets [ ] instead of parentheses ( ).
*** See the bottom of this document for the declaration of the reference variables
*** for contributors-url, forks-url, etc. This is an optional, concise syntax you may use.
*** https://www.markdownguide.org/basic-syntax/#reference-style-links
-->
[![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]
<!-- PROJECT LOGO -->
<br />
<p align="center">
<a href="https://github.com/github_username/repo_name">
<img src="images/logo.png" alt="Logo" width="100%">
</a>
<h3 align="center">RandBot</h3>
<h2>PROJECT DISCONTINUED due to lack of interest</h2>
<p align="center">
Discord bot randomizer. Users can create a list that is stored in MongoDB and the bot can select a random item from this list.
·
<a href="https://github.com/karlji/RandBot-Discord-Randomizer/issues">Report Bug</a>
·
<a href="https://github.com/karlji/RandBot-Discord-Randomizer/issues">Request Feature</a>
</p>
</p>
<!-- TABLE OF CONTENTS -->
<details open="open">
<summary><h2 style="display: inline-block">Table of Contents</h2></summary>
<ol>
<li>
<a href="#about-the-project">About The Project</a>
<ul>
<li><a href="#built-with">Built With</a></li>
</ul>
</li>
<li>
<a href="#getting-started">Getting Started</a>
<ul>
<li><a href="#prerequisites">Prerequisites</a></li>
<li><a href="#installation">Installation</a></li>
</ul>
</li>
<li><a href="#commands">Bot Commands</a></li>
<li><a href="#roadmap">Roadmap</a></li>
<li><a href="#contributing">Contributing</a></li>
<li><a href="#license">License</a></li>
<li><a href="#contact">Contact</a></li>
</ol>
</details>
<!-- ABOUT THE PROJECT -->
## 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 -->
## 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
- <img src="images/discord_token.png" alt="Logo" width="150px">
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
- <img src="images/mongo_token.png" alt="Logo" width="600px">
- <img src="images/mongo_document.png" alt="Logo" width="600px">
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.
- <img src="images/oauth.png" alt="Logo" width="600px">
<!-- COMMANDS -->
## 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 -->
## Roadmap
See the [open issues](https://github.com/github_username/repo_name/issues) for a list of proposed features (and known issues).
<!-- CONTRIBUTING -->
## 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 -->
## License
Distributed under the MIT License. See `LICENSE` for more information.
<!-- CONTACT -->
## 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)
<!-- MARKDOWN LINKS & IMAGES -->
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
[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/

157
commands.py Normal file
View File

@ -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

BIN
images/discord_token.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

BIN
images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

BIN
images/mongo_document.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
images/mongo_token.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
images/oauth.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

142
messages.py Normal file
View File

@ -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

54
randbot.py Normal file
View File

@ -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)

4
requirements.txt Normal file
View File

@ -0,0 +1,4 @@
discord
pymongo
asyncio
dnspython

2
tokens.py Normal file
View File

@ -0,0 +1,2 @@
mongo_token = ''
discord_token = ''