Skip to content
This repository has been archived by the owner on Jan 26, 2024. It is now read-only.

Commit

Permalink
Merge pull request #6 from ilyash0/dev
Browse files Browse the repository at this point in the history
v0.3.1
  • Loading branch information
ilyash0 authored Sep 11, 2023
2 parents 4ee8fbf + 9555c63 commit 8027a53
Show file tree
Hide file tree
Showing 11 changed files with 199 additions and 28 deletions.
1 change: 1 addition & 0 deletions bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# parser.add_argument('-l', '--lang', action='store', default='en', help='Discord bot language',
# choices=['en', 'fr', 'pt', 'es', 'de', 'ru'])
parser.add_argument('-t', '--token', action='store', help='Token for Discord bot')
parser.add_argument('-d', '--defer', action='store_true', help='Enable defer response')

database_group = parser.add_argument_group('database')
database_group.add_argument('-da', '--database-address', action='store',
Expand Down
4 changes: 3 additions & 1 deletion bot/cogs/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ async def pay(self, inter: ApplicationCommandInteraction,
receiver: str = Param(description='Получатель (его ник в игре)'),
amount: int = Param(description='Количество монет'),
message: str = Param(default=None, description='Сообщение получателю')):
await inter.response.defer()
p: Penguin = await getPenguinFromInter(inter)
receiverId = await Penguin.select('id').where(Penguin.username == receiver.lower()).gino.first()

Expand All @@ -78,6 +79,7 @@ async def pay2(self, inter: ApplicationCommandInteraction,
receiver: disnake.User = Param(description='Получатель'),
amount: int = Param(description='Количество монет'),
message: str = Param(default=None, description='Сообщение получателю')):
await inter.response.defer()
p: Penguin = await getPenguinFromInter(inter)
r: Penguin = await getPenguinOrNoneFromUserId(receiver.id)
if r is None:
Expand All @@ -88,7 +90,7 @@ async def pay2(self, inter: ApplicationCommandInteraction,
return await inter.send(statusDict["message"], ephemeral=True)

await inter.send(statusDict["message"])
await notifyCoinsReceive(p, r, amount, message)
await notifyCoinsReceive(p, r, amount, message, "pay2")

@slash_command(name="online", description="Показывает количество игроков которые сейчас онлайн")
async def online(self, inter: ApplicationCommandInteraction):
Expand Down
97 changes: 94 additions & 3 deletions bot/cogs/private.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
from datetime import timedelta, datetime
from statistics import mean

import disnake
from disnake import ApplicationCommandInteraction, AllowedMentions, Webhook
from disnake.ext.commands import Cog, slash_command
from disnake import ApplicationCommandInteraction, AllowedMentions, Webhook, Embed
from disnake.ext.commands import Cog, slash_command, Param
from loguru import logger

from bot.data.clubpenguin.penguin import Login, Penguin
from bot.data.clubpenguin.transactions import Transactions
from bot.handlers.buttons import Rules
from bot.misc.constants import (
embedRuleImageRu,
embedRuleRu,
embedAboutImage,
embedAbout,
guild_ids,
avatarImageBytearray)
avatarImageBytearray, placeholderImageLink)
from bot.handlers.select import About


Expand Down Expand Up @@ -53,6 +58,92 @@ async def about(self, inter: ApplicationCommandInteraction):
await webhook.send(embeds=[embedAboutImage, embedAbout], view=view)
await inter.send("Success", ephemeral=True)

@slash_command(name="statistics", description="Показывает игровую статистику", guild_ids=guild_ids)
async def online(self, inter: ApplicationCommandInteraction,
start_date: str = Param(description='Дата отсчёта, формата ДД.ММ.ГГГГ'),
end_date: str = Param(default=None, description='Дата окончания, формата ДД.ММ.ГГГГ'),
detail: str = Param(default="No", choices=["Yes", "No"], description='Показать доп. информацию')):
await inter.response.defer()
try:
start_date_obj = datetime.strptime(start_date, "%d.%m.%Y").date()
if end_date:
end_date_obj = datetime.strptime(end_date, "%d.%m.%Y").date()
title = f"с {start_date_obj.strftime('%d.%m.%Y')} по {end_date_obj.strftime('%d.%m.%Y')}"
else:
end_date_obj = start_date_obj + timedelta(days=1)
title = f"{start_date_obj.strftime('%d.%m.%Y')} \n"
except ValueError:
return await inter.send(f"Дата `{start_date}` невалидна. Ожидается формат `ДД.ММ.ГГГГ`.")

if start_date_obj < datetime(2020, 9, 28).date():
return await inter.send(
f"За указанный период данные отсутствуют. Самая ранняя доступная дата — `28.09.2020`")

embed = Embed(color=0x2B2D31, title=title)
embed.set_footer(text="Если день ещё не закончился, то данные могут быть не верные")
# embed.set_thumbnail("https://static10.tgstat.ru/channels/_0/49/49155b0c3f227c470ffa63db3f8923a2.jpg")
embed.description = "## Активность игроков\n"
embed.set_image(url=placeholderImageLink)

logins_list = await Login.query.where((Login.date >= start_date_obj) & (Login.date <= end_date_obj)).gino.all()
online_list = {}
active_players = set()
total_time_played = 0
for login in logins_list:
if login.minutes_played == 0: continue

active_players.add(login.penguin_id)
total_time_played += login.minutes_played
time_int = (login.date.replace(second=0, microsecond=0) - datetime(1970, 1, 1)).total_seconds() / 60
for i in range(login.minutes_played):
try:
if not online_list.get(str(time_int + i)):
online_list[str(time_int + i)] = 1
else:
online_list[str(time_int + i)] += 1
except KeyError:
pass

max_online = max(online_list.values())
max_online_date = timedelta(
minutes=float(next(key for key, value in online_list.items() if value == max_online))) + datetime(1970, 1,
1)

embed.description += f"""Максимальный онлайн: **{max_online}** ({max_online_date.strftime('%d.%m.%Y %H:%M')})
Средний онлайн: **{round(mean(online_list.values()), 3)}**
Среднее время на игрока: **{round(total_time_played / max(len(active_players), 1), 3)}** минут"""

if detail == "Yes":
new_accounts = await Penguin.query.where(
(Penguin.registration_date >= start_date_obj) & (Penguin.registration_date <= end_date_obj)).gino.all()
returning_players = set()
for p in new_accounts:
if p.minutes_played > 30:
returning_players.add(p.id)
# for p in new_accounts:
# count = sum(1 for login in logins_list if login.penguin_id == p.id)
# if count > 2:
# returning_players.add(p.id)

all_accounts = len(await Penguin.query.gino.all())
transactions_list = await Transactions.query.where(
(Transactions.time >= start_date_obj) & (Transactions.time <= end_date_obj)).gino.all()
all_rub = 0
paying_accounts = set()
for transaction in transactions_list:
all_rub += transaction.rub
paying_accounts.add(transaction.penguin_id)

embed.description += f"""
Зарегистрировано новых аккаунтов: **{f'{len(new_accounts):,}'.replace(',', ' ')}**
Удержание пользователей: **{round((len(returning_players) / len(new_accounts) * 100), 1)}**%\n"""
embed.description += f"""## Экономические данные
Общая выручка от микротранзакций: **||{f'{max(all_rub, 0):,}'.replace(',', ' ')}||** ₽
ARPU: **||{round(all_rub / all_accounts, 3) if all_rub > 0 else 0}||** ₽
ARPPU: **||{round(all_rub / len(paying_accounts), 3) if all_rub > 0 else 0}||** ₽\n"""

await inter.send(embeds=[embed])


def setup(bot):
bot.add_cog(PrivateCommands(bot))
22 changes: 19 additions & 3 deletions bot/core/puffleBot.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import os
import traceback

import disnake
from disnake import Webhook, Game
from disnake.ext.commands import InteractionBot
from disnake import Webhook, Game, ApplicationCommandInteraction
from disnake.ext.commands import InteractionBot, CommandError
from loguru import logger
import bot.cogs
from bot.data.pufflebot.fundraising import Fundraising, FundraisingBackers
Expand All @@ -13,8 +14,9 @@


class PuffleBot(InteractionBot):
def __init__(self, *args, **kwargs):
def __init__(self, defer, *args, **kwargs):
super().__init__(*args, **kwargs)
self.defer = defer

def load_cogs(self):
for file in os.listdir(bot.cogs.__path__[0]):
Expand All @@ -26,6 +28,11 @@ async def on_ready(self):

logger.info(f"Bot {self.user} ready")

async def on_slash_command(self, inter: ApplicationCommandInteraction):
logger.debug(f"Used slash command /{inter.data.name} in #{inter.channel}")
if self.defer:
await inter.response.defer()

async def on_connect(self):
logger.info(f'Bot connected')

Expand All @@ -49,3 +56,12 @@ async def on_connect(self):
self.add_view(FundraisingButtons(fundraising, message, p, backers), message_id=message.id)
except disnake.NotFound:
await fundraising.delete()

async def on_slash_command_error(self, inter: ApplicationCommandInteraction, exception: CommandError):
logger.error(exception)
traceback.print_exception(type(exception), exception, exception.__traceback__)

async def on_error(self, event_method: str, *args, **kwargs):
# logger.error(f"ERROR: {event_method}")
logger.error(f"Ignoring exception in {event_method}")
traceback.print_exc()
2 changes: 1 addition & 1 deletion bot/core/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ async def start(self):
command_sync_flags = CommandSyncFlags.default()
command_sync_flags.sync_commands = True

self.bot = PuffleBot(intents=intents, command_sync_flags=command_sync_flags,
self.bot = PuffleBot(defer=self.config.defer, intents=intents, command_sync_flags=command_sync_flags,
owner_id=527140180696629248) # test_guilds=[755445822920982548],
self.bot.load_cogs()

Expand Down
12 changes: 12 additions & 0 deletions bot/data/clubpenguin/transactions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from bot.data import db_cp


class Transactions(db_cp.Model):
__tablename__ = 'transactions'

id = db_cp.Column(db_cp.Integer, primary_key=True,
server_default=db_cp.text("nextval('\"login_id_seq\"'::regclass)"))
penguin_id = db_cp.Column(db_cp.ForeignKey('penguin.id', ondelete='CASCADE', onupdate='CASCADE'), nullable=False)
time = db_cp.Column(db_cp.DateTime, nullable=False, server_default=db_cp.text("now()"))
description = db_cp.Column(db_cp.CHAR(255), nullable=False)
rub = db_cp.Column(db_cp.Numeric(15, 2), nullable=False, server_default=db_cp.text("0"))
8 changes: 4 additions & 4 deletions bot/handlers/buttons.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from bot.handlers.modals import FundraisingModal
from bot.handlers.notification import notifyCoinsReceive
from bot.misc.constants import embedRuleImageRu, embedRuleRu, embedRuleImageEn, embedRuleEn, embedRolesRu, \
embedRolesEn, enFullRulesLink, ruFullRulesLink
embedRolesEn, enFullRulesLink, ruFullRulesLink, embedRoles2Ru, embedRoles2En
from bot.misc.penguin import Penguin
from bot.misc.utils import getPenguinFromInter, transferCoinsAndReturnStatus

Expand Down Expand Up @@ -57,7 +57,7 @@ async def donate(self, inter: disnake.CommandInteraction, coins: int):

await self.message.edit(embed=embed)
await self.fundraising.update(raised=self.raised).apply()
await notifyCoinsReceive(p, self.receiver, int(coins))
await notifyCoinsReceive(p, self.receiver, int(coins), command="fundraising")

@disnake.ui.button(label="100", style=disnake.ButtonStyle.blurple, emoji="<:coin:788877461588279336>",
custom_id="100")
Expand Down Expand Up @@ -155,10 +155,10 @@ async def translate(self, button: disnake.ui.Button, inter: disnake.CommandInter
self.language = "Ru"
button.label = "Translate"
button.emoji = "🇺🇸"
await self.inter.edit_original_response(embeds=[embedRolesRu], view=self)
await self.inter.edit_original_response(embeds=[embedRolesRu, embedRoles2Ru], view=self)
elif self.language == "Ru":
self.language = "En"
button.label = "Перевести"
button.emoji = "🇷🇺"
await self.inter.edit_original_response(embeds=[embedRolesEn], view=self)
await self.inter.edit_original_response(embeds=[embedRolesEn, embedRoles2En], view=self)
await inter.response.defer()
12 changes: 9 additions & 3 deletions bot/handlers/notification.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ async def check_membership():
continue


async def notifyCoinsReceive(senderPenguin: Penguin, receiverPenguin: Penguin, coins, message=None):
async def notifyCoinsReceive(senderPenguin: Penguin, receiverPenguin: Penguin, coins, message=None, command=None):
receiverId = await PenguinIntegrations.select("discord_id").where(
PenguinIntegrations.penguin_id == receiverPenguin.id).gino.first()
receiver = await bot.fetch_user(int(receiverId[0]))
Expand All @@ -43,12 +43,18 @@ async def notifyCoinsReceive(senderPenguin: Penguin, receiverPenguin: Penguin, c
return

embed = Embed(color=0xB7F360, title=f"Пингвин под ником «{senderPenguin.safe_name()}» перевел(а) Вам {coins}м")
embed.set_thumbnail(f"https://play.cpps.app/avatar/{receiverPenguin.id}/cp?size=600")
if message:
embed.add_field("Сообщение", message, inline=False)
if command == "fundraising":
embed.add_field("Команда", "</fundraising:1133131135539494962>", inline=False)
if command == "pay2":
embed.add_field("Команда", "</pay2:1129711949576409188>", inline=False)
else:
embed.add_field("Команда", "</pay:1099629339110289445>", inline=False)
embed.add_field("Баланс", f"{receiverPenguin.coins} {emojiCoin}", inline=False)
embed.add_field("Пользователь", f"{sender.mention}", inline=False)
embed.set_footer(text=f"Ваш аккаунт: {receiverPenguin.safe_name()}",
icon_url=f"https://play.cpps.app/avatar/{receiverPenguin.id}/cp?size=600")
embed.set_footer(text=f"Ваш аккаунт: {receiverPenguin.safe_name()}")
await sendNotify(receiver, embed)


Expand Down
4 changes: 2 additions & 2 deletions bot/handlers/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from disnake.ui import Select

from bot.handlers.buttons import RulesEphemeral, Roles
from bot.misc.constants import embedLinks, embedRuleImageRu, embedRuleRu, embedRolesRu
from bot.misc.constants import embedLinks, embedRuleImageRu, embedRuleRu, embedRolesRu, embedRoles2Ru
from bot.misc.penguin import Penguin


Expand Down Expand Up @@ -41,7 +41,7 @@ async def callback(self, inter: MessageInteraction):
allowed_mentions=AllowedMentions(roles=False, users=False))
await inter.edit_original_response(view=RulesEphemeral(inter))
elif inter.values[0] == "Roles":
await inter.send(ephemeral=True, embeds=[embedRolesRu],
await inter.send(ephemeral=True, embeds=[embedRolesRu, embedRoles2Ru],
allowed_mentions=AllowedMentions(roles=False, users=False))
await inter.edit_original_response(view=Roles(inter))

Expand Down
34 changes: 26 additions & 8 deletions bot/misc/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,31 +102,49 @@
> **{emojiGameFavicon} • GAME – https://cpps.app/**
> **{emojiWikiFavicon} • WIKI – https://wiki.cpps.app/**
> **CARD-JITSU (PRE-ALPHA) – https://cpps.app/games/cj/**
"""
embedLinks.set_image(url=placeholderImageLink)

# Roles embeds
embedRolesRu = Embed(color=0x2B2D31)
embedRolesRu.description = """
```Роли-достижения (ACHIEVEMENTS):```
```Роли-достижения```
<@&860176034834153543> — Победитель конкурса «Рейтинг активности».
<@&792899688608038913> — [Nitro бустеры](https://support.discord.com/hc/ru/articles/360028038352) сервера.
"""
embedRolesRu.set_image(url=placeholderImageLink)
embedRoles2Ru = Embed(color=0x2B2D31)
embedRoles2Ru.description = """
```Роли персонажей```
<@&860125656427003905> — Владельцы популярного бизнеса в Клубе Пингвинов.
<@&860125656427003905> — Пингвины, владеющие популярным бизнесом.
<@&1124784263754158090> — Игроки, которые прошли *тест экскурсовода* в игре.
<@&1124784263754158090> — Игроки, получившие *шляпу экскурсовода* в игре.
<@&1139243562798698496> — Пингвины, овладевшие искусством ниндзя и *одолевшие сенсея*.
<@&1135929982544252979> — Члены *тайной организации* по защите острова.
"""
embedRolesRu.set_image(url=placeholderImageLink)
embedRoles2Ru.set_image(url=placeholderImageLink)
embedRolesEn = Embed(color=0x2B2D31)
embedRolesEn.description = """
```Achievements roles:```
```Achievements roles```
<@&860176034834153543> — The winner of the «Activity Rating» contest.
<@&792899688608038913> — [Nitro boosters](https://support.discord.com/hc/en-us/articles/360028038352).
"""
embedRolesEn.set_image(url=placeholderImageLink)
embedRoles2En = Embed(color=0x2B2D31)
embedRoles2En.description = """
```Character roles```
<@&860125656427003905> - Owners of a popular business in Club Penguin.
<@&860125656427003905> — Penguins who own a popular business.
<@&1124784263754158090> - Players who have passed the *guide test* in the game.
<@&1124784263754158090> — Players who have received a *tour guide hat* in the game.
<@&1139243562798698496> - Penguins who have mastered the art of the ninja and *defeated the sensei*.
<@&1135929982544252979> - Members of a *secret organization* to save the island.
"""
embedRolesEn.set_image(url=placeholderImageLink)
embedRoles2En.set_image(url=placeholderImageLink)
Loading

0 comments on commit 8027a53

Please sign in to comment.