This repository has been archived by the owner on Oct 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update the bot, now it have more statistics (#3)
* Initial push of the bot v2.0.0
- Loading branch information
1 parent
01e2ce9
commit ca442c7
Showing
5 changed files
with
353 additions
and
279 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import discord | ||
import os | ||
from PIL import Image | ||
from datetime import datetime | ||
import csv | ||
import pandas as pd | ||
import numpy as np | ||
import matplotlib.pyplot as plt | ||
|
||
def loadCSV(ctx, data): | ||
try: | ||
with open(f"{ctx.guild.name}/roll.csv", mode="r", newline="") as file: | ||
reader = csv.reader(file) | ||
for i, row in enumerate(reader): | ||
if i == 0: | ||
continue | ||
data[row[0]] = [float(x) for x in row[1:]] | ||
return data | ||
except FileNotFoundError: | ||
print("File not found, creating the file") | ||
with open(f"{ctx.guild.name}/roll.csv", mode="w", newline=""): | ||
pass | ||
|
||
def update_csv(ctx, name, value, data): | ||
size = 0 | ||
for _, values in data.items(): | ||
if size < len(values): | ||
size = len(values) | ||
if name in data: | ||
data[name].append(value) | ||
else: | ||
data[name] = [value] | ||
with open(f"{ctx.guild.name}/roll.csv", mode="w", newline="") as file: | ||
writer = csv.writer(file) | ||
writer.writerow(["Name"] + [f"Value_{i}" for i in range(0, size+1)]) | ||
for name, values in data.items(): | ||
writer.writerow([name] + values) | ||
return data | ||
|
||
|
||
|
||
def stat(name, data): | ||
name_data = data.loc[data['Name'] == name].iloc[:, 1:].values | ||
name_values = name_data[~np.isnan(name_data)] | ||
name_sum = name_values.sum() | ||
name_mean = name_values.mean() | ||
name_min = name_values.min() | ||
name_max = name_values.max() | ||
name_median = np.median(name_values) | ||
name_std_dev = name_values.std() | ||
bins = np.arange(0, 110, 10) | ||
name_frequency, _ = np.histogram(name_values, bins=bins) | ||
name_frequency = name_frequency.tolist() | ||
name_variance = name_values.var() | ||
|
||
name_stats = { | ||
'Name': name, | ||
'Number of Rolls': len(name_values), | ||
'Sum': name_sum, | ||
'Mean': name_mean, | ||
'Min': name_min, | ||
'Max': name_max, | ||
'Median': name_median, | ||
'Standard Deviation': name_std_dev, | ||
'Frequency': name_frequency, | ||
'Variance': name_variance, | ||
} | ||
|
||
return name_stats | ||
|
||
def plot_aux(ctx, name, data): | ||
bins = np.arange(0, 110, 10) | ||
plot_data = stat(name, data) | ||
fig, ax = plt.subplots() | ||
plt.bar(bins[:-1]+5, plot_data['Frequency'], width=10, color='aqua', edgecolor='black', alpha=0.3, label=name + ' Frequency') | ||
plt.axvline(plot_data['Mean'], color='r', linestyle='--', label=name + ' Mean') | ||
plt.axvline(plot_data['Median'], color='g', linestyle='-.', label=name + ' Median') | ||
plt.axvline(plot_data['Min'], color='black', linestyle=':', label=name + ' Min') | ||
plt.axvline(plot_data['Max'], color='blue' ,linestyle=':', label=name + ' Max') | ||
plt.legend() | ||
ax.set_xlabel('Value') | ||
ax.set_ylabel('Frequency') | ||
ax.set_xticks(bins) | ||
ax.set_title(f'{name}_statistics({plot_data["Number of Rolls"]} rolls)') | ||
#plt.show() | ||
fig.savefig(f"{ctx.guild.name}/users/{name}_plot.png") | ||
|
||
|
||
|
||
|
||
|
||
def send_to_discord(ctx): | ||
images = [] | ||
|
||
file_list = os.listdir(f"{ctx.guild.name}/users/") | ||
|
||
png_files = [f for f in file_list if f.endswith('.png')] | ||
|
||
for file_name in png_files: | ||
file_path = os.path.join(f"{ctx.guild.name}/users/", file_name) | ||
image = Image.open(file_path) | ||
images.append(image) | ||
|
||
total_width = sum(image.width for image in images) | ||
max_height = max(image.height for image in images) | ||
|
||
result_image = Image.new('RGB', (total_width, max_height)) | ||
x_offset = 0 | ||
for image in images: | ||
result_image.paste(image, (x_offset, 0)) | ||
x_offset += image.width | ||
|
||
result_image.save(f"{ctx.guild.name}/statistic.png") | ||
|
||
result_file = discord.File(f"{ctx.guild.name}/statistic.png") | ||
|
||
return result_file | ||
|
||
|
||
def stat_player_value(ctx, data): | ||
X = data.iloc[:, 1:].values | ||
labels = data.iloc[:, 0].values | ||
fig , ax= plt.subplots() | ||
for i in range(labels.size): | ||
plt.plot(X[i], label=labels[i]) | ||
ax.set_xlabel('Number of Rolls') | ||
ax.set_ylabel('Value') | ||
plt.title('Rolls Comparison') | ||
plt.legend() | ||
#plt.show() | ||
fig.savefig(f"{ctx.guild.name}/compare.png") | ||
|
||
return f"{ctx.guild.name}/compare.png" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
import discord | ||
|
||
from discord.ext import commands | ||
import random | ||
import numpy as np | ||
import locale | ||
import matplotlib.pyplot as plt | ||
import os | ||
from PIL import Image | ||
from datetime import datetime | ||
import csv | ||
import pandas as pd | ||
from bot.commands_aux import * | ||
import shutil | ||
|
||
lang = locale.getdefaultlocale() | ||
if lang[0] == 'fr_FR': | ||
from lang.fr import * | ||
|
||
elif lang[0] == 'en_US': | ||
from lang.en import * | ||
elif lang[0] == 'sr_RS': | ||
from lang.sr import * | ||
else: | ||
from lang.template import * | ||
|
||
|
||
import json | ||
with open('config.json') as f: | ||
config = json.load(f) | ||
|
||
TOKEN = config['token'] | ||
PREFIX = config['prefix'] | ||
MIN = config['min'] | ||
|
||
class Mybot(commands.Bot): | ||
async def on_ready(self): | ||
activity = discord.Game(name='RP', type=discord.ActivityType.playing) | ||
await bot.change_presence(status=discord.Status.dnd, activity=activity) | ||
print(login + f'{self.user} (ID: {self.user.id})') | ||
print('------') | ||
for i in bot.guilds: | ||
print(connected_on + i.name) | ||
print('------') | ||
|
||
|
||
intents = discord.Intents.default() | ||
intents.message_content = True | ||
bot = Mybot(command_prefix=PREFIX,intents=intents) | ||
|
||
data = {} | ||
|
||
@bot.command( | ||
description='This command rolls a random number between 0 and the specified number (default: 100). For example, to roll a number between 0 and 50, you can use the command "!r 50".', | ||
help='This command rolls a random number between 0 and the specified number (default: 100). For example, to roll a number between 0 and 50, you can use the command "!r 50".' | ||
) | ||
async def r(ctx, number: int = 100): | ||
if not os.path.exists(ctx.guild.name): | ||
os.makedirs(ctx.guild.name) | ||
loadCSV(ctx, data) | ||
value = random.randrange(MIN, number+1) | ||
_value = value/number*100 | ||
user = ctx.author.display_name | ||
|
||
update_csv(ctx, user, _value, data) | ||
x = await ctx.send(f"```{ctx.author.display_name} "+ made + str(value) + space + on + "[" + str(number) + "]```") | ||
await ctx.message.delete() | ||
|
||
if value == MIN: | ||
await x.add_reaction('\N{CROSS MARK}') | ||
elif value == number: | ||
await x.add_reaction('\N{WHITE HEAVY CHECK MARK}') | ||
|
||
|
||
@bot.command( | ||
description='Deletes all images generated by !stat command in the current server', | ||
help='This command deletes all images generated by !stat command in the current server. To use this command, type "!clear_images".' | ||
) | ||
async def clear_image(ctx): | ||
file_list = os.listdir(f"{ctx.guild.name}/users/") | ||
png_files = [f for f in file_list if f.endswith('.png')] | ||
for file_name in png_files: | ||
file_path = os.path.join(f"{ctx.guild.name}/users/", file_name) | ||
os.remove(file_path) | ||
os.remove(f"{ctx.guild.name}/statistic.png") | ||
await ctx.message.delete() | ||
await ctx.send('Clear successful !') | ||
|
||
|
||
@bot.command( | ||
name='save', | ||
description='Saves the current plot data to a folder', | ||
help='This command saves the current data, you can use the command "!s".' | ||
) | ||
async def save(ctx): | ||
now = datetime.now() | ||
folder_name = now.strftime("%Y-%m-%d %H.%M") | ||
|
||
file_path = ctx.guild.name+"/save/" + folder_name | ||
|
||
# Create the save folder if it doesn't exist | ||
if not os.path.exists(file_path): | ||
os.makedirs(file_path) | ||
|
||
try: | ||
file_list = os.listdir(f"{ctx.guild.name}/users/") | ||
# Calculate the total width and maximum height of the result image | ||
png_files = [f for f in file_list if f.endswith('.png')] | ||
for file_name in png_files: | ||
# Save the png image to a file in the save folder | ||
with open(ctx.guild.name+ "/users/" + file_name, 'rb') as f: | ||
image = f.read() | ||
with open(os.path.join(file_path, file_name), 'wb') as f: | ||
f.write(image) | ||
|
||
with open(ctx.guild.name + "/statistic.png", 'rb') as f: | ||
image = f.read() | ||
with open(os.path.join(file_path, "statistic.png"), 'wb') as f: | ||
f.write(image) | ||
with open(ctx.guild.name + "/compare.png", 'rb') as f: | ||
image = f.read() | ||
with open(os.path.join(file_path, "compare.png"), 'wb') as f: | ||
f.write(image) | ||
|
||
|
||
shutil.copy2(f"{ctx.guild.name}/roll.csv", file_path) | ||
# Send a message to confirm that the save was successful | ||
await ctx.send('Files saved to the save folder!') | ||
except: | ||
await ctx.send(f'There is no data, try running the {PREFIX}plot, {PREFIX}stat commands for saving all data.') | ||
|
||
|
||
@bot.command( | ||
name='plot', | ||
description='Plots the data of the current server', | ||
help='This command plots the data of the current server, you can use the command "!plot", you can also specify a user to plot his data, for example "!plot @user".' | ||
) | ||
async def plot(ctx, user : discord.User = None): | ||
try: | ||
if not os.path.exists(f"{ctx.guild.name}/users/"): | ||
os.makedirs(f"{ctx.guild.name}/users/") | ||
ctx_data = pd.read_csv(f"{ctx.guild.name}/roll.csv", sep=',') | ||
for names in ctx_data['Name']: | ||
plot_aux(ctx, names, ctx_data) | ||
|
||
x = send_to_discord(ctx) | ||
if user is not None: | ||
await ctx.send(file=discord.File(f"{ctx.guild.name}/users/{user.display_name}_plot.png")) | ||
else: | ||
await ctx.send(file = x) | ||
except FileNotFoundError: | ||
await ctx.send(f'There is no data, try running the {PREFIX}r command.') | ||
|
||
|
||
|
||
@bot.command( | ||
name='stat', | ||
description='Compare players rolls', | ||
help='This command Compare players rolls.' | ||
) | ||
async def stat(ctx): | ||
try: | ||
ctx_data = pd.read_csv(f"{ctx.guild.name}/roll.csv", sep=',') | ||
x = stat_player_value(ctx, ctx_data) | ||
await ctx.send(file=discord.File(x)) | ||
except FileNotFoundError: | ||
await ctx.send(f'There is no data, try running the {PREFIX}r command.') | ||
|
||
|
||
@bot.event | ||
async def on_command_error(ctx, error): | ||
command_list = '\n'.join([f'\t{c.name}: {c.description}' for c in bot.commands]) | ||
|
||
# Check if the error is a CommandNotFound error | ||
if isinstance(error, commands.CommandNotFound): | ||
# If the command is not found, send the help message to the user | ||
await ctx.send(f'```Sorry, I don\'t recognize that command. Here is a list of the commands that I know:\n{command_list}```') | ||
else: | ||
# For other types of errors, print the error to the console | ||
print(error) | ||
|
||
|
||
@bot.event | ||
async def on_reaction_add(reaction, user): | ||
emoji = reaction.emoji | ||
if user.bot: | ||
return | ||
|
||
if emoji == '\N{CROSS MARK}': | ||
await reaction.message.reply(str(user.display_name) + space + laught) | ||
elif emoji == '\N{WHITE HEAVY CHECK MARK}': | ||
await reaction.message.reply(str(user.display_name) + space + congratulate) | ||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
bot.run(TOKEN) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"token": "Your_Bot_Token_Or_Ask_Me_For_It", | ||
"prefix": "!", | ||
"min": 1 | ||
} |
Oops, something went wrong.