Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor #3

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file removed bot/__init__.py
Empty file.
18 changes: 16 additions & 2 deletions bot/__main__.py → discordbot/__main__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
import asyncio
from argparse import ArgumentParser

import discord.utils
from bot.client import create_client
from discordbot.client import create_client
from httpx import AsyncClient

from .settings import StandaloneSettings

settings = StandaloneSettings()
parser = ArgumentParser()
parser.add_argument("--lnbits-admin-key", required=False)
parser.add_argument("--lnbits-url", required=False)


async def run():
args = parser.parse_args()

settings = StandaloneSettings()
if args.lnbits_admin_key:
settings.lnbits_admin_key = args.lnbits_admin_key
if args.lnbits_url:
settings.lnbits_url = args.lnbits_url

assert settings.lnbits_admin_key, "Admin key is required"
assert settings.lnbits_url, "Lnbits url is required"

async with AsyncClient() as http:
if not settings.data_folder.is_dir():
settings.data_folder.mkdir()
Expand Down
56 changes: 34 additions & 22 deletions bot/api.py → discordbot/api.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import json
from typing import Optional, Union

import discord
Expand Down Expand Up @@ -37,28 +38,25 @@ async def get_lnbits_user(self, discord_user: DiscordUser):
"/users",
self.admin_key,
extension="usermanager",
params={"extra.discord_id": str(discord_user.id)},
params={"extra": json.dumps({"discord_id": str(discord_user.id)})},
)
if users:
user = users[0]
if (
user["extra"].get("discord_avatar_url")
!= discord_user.display_avatar.url
):
try:
await self.request(
"PATCH",
f'/users/{user["id"]}',
self.admin_key,
extension="usermanager",
json={
"extra": {
"discord_avatar_url": discord_user.display_avatar.url,
}
},
)
except HTTPStatusError:
pass
await self.request(
"PATCH",
f'/users/{user["id"]}',
self.admin_key,
extension="usermanager",
json={
"extra": {
"discord_avatar_url": discord_user.display_avatar.url,
}
},
)
return user

async def get_user_wallet(self, discord_user: DiscordUser) -> Optional[Wallet]:
Expand All @@ -72,29 +70,38 @@ async def get_user_wallet(self, discord_user: DiscordUser) -> Optional[Wallet]:
self.admin_key,
extension="usermanager",
)
if not wallets:
await self.request(
"POST",
f"/wallets",
self.admin_key,
extension="usermanager",
json={"wallet_name": f"{discord_user.name}-main"},
)

wallet = Wallet(**wallets[0])
self.wallet_cache[discord_user] = wallet
return wallet

async def get_user_balance(self, discord_user: DiscordUser) -> Optional[int]:
async def get_user_balance(
self, discord_user: DiscordUser, _retry=True
) -> Optional[int]:
wallet = await self.get_user_wallet(discord_user)
try:
wallet = await self.request("GET", "/wallet", wallet.adminkey)
if wallet:
return int(wallet["balance"] / 1000)
except HTTPStatusError:
# Try again after clearing cache
if discord_user in self.wallet_cache:
self.wallet_cache.pop(discord_user)
return await self.get_user_balance(discord_user)
if _retry and discord_user in self.wallet_cache.pop(discord_user, None):
return await self.get_user_balance(discord_user, _retry=False)
else:
raise

async def get_or_create_wallet(self, user: DiscordUser) -> Wallet:
wallet = await self.get_user_wallet(user)
if not wallet:
user = await self.request(
new_user = await self.request(
"POST",
"/users",
self.admin_key,
Expand All @@ -108,7 +115,8 @@ async def get_or_create_wallet(self, user: DiscordUser) -> Wallet:
},
),
)
wallet = Wallet(**user["wallets"][0])
wallet = Wallet(**new_user["wallets"][0])
self.wallet_cache[user] = wallet
return wallet

async def request(
Expand All @@ -131,7 +139,11 @@ async def request(
return response.json()

async def send_payment(
self, sender: discord.Member, receiver: discord.Member, amount: int, memo: str
self,
sender: DiscordUser,
receiver: DiscordUser,
amount: int,
memo: Optional[str] = None,
):
sender_wallet = await self.get_user_wallet(sender)

Expand Down
22 changes: 11 additions & 11 deletions bot/client.py → discordbot/client.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from __future__ import annotations

import logging
import os.path
import random
from typing import TYPE_CHECKING, Union
from typing import TYPE_CHECKING, Optional, Union

import discord
import discord.utils
Expand All @@ -21,8 +22,6 @@
get_amount_str,
)

discord.utils.setup_logging()

if discord_settings.discord_dev_guild:
DEV_GUILD = discord.Object(id=discord_settings.discord_dev_guild)
else:
Expand Down Expand Up @@ -63,7 +62,7 @@ async def try_send_payment_notification(
sender: Union[discord.Member, discord.User],
receiver: Union[discord.Member, discord.User],
amount: int,
memo: str = None,
memo: Optional[str] = None,
):
receiver_wallet = await self.api.get_user_wallet(receiver)
new_balance = await self.api.get_user_balance(receiver)
Expand Down Expand Up @@ -125,8 +124,7 @@ def create_client(admin_key: str, http: AsyncClient, lnbits_url: str, data_folde

@client.event
async def on_ready():
print(f"Logged in as {client.user} (ID: {client.user.id})")
print("------")
logging.info(f"Logged in as {client.user} (ID: {client.user.id})")
await client.api.request(
"PATCH",
"/bot",
Expand All @@ -152,13 +150,12 @@ async def create(interaction: LnbitsInteraction):

@client.tree.command(name="balance", description="Check the balance of your wallet")
async def balance(interaction: LnbitsInteraction):
# await interaction.response.defer(ephemeral=True)

wallet = await client.api.get_user_wallet(interaction.user)
await interaction.response.defer(ephemeral=True)

wallet = await client.api.get_or_create_wallet(interaction.user)
balance = await client.api.get_user_balance(interaction.user)

await interaction.response.send_message(
await interaction.followup.send(
ephemeral=True,
content=f"Your balance: **{get_amount_str(balance)}**",
view=discord.ui.View().add_item(
Expand All @@ -174,7 +171,10 @@ async def balance(interaction: LnbitsInteraction):
)
@app_commands.guild_only()
async def tip(
interaction: LnbitsInteraction, member: discord.Member, amount: int, memo: str
interaction: LnbitsInteraction,
member: discord.Member,
amount: int,
memo: Optional[str] = None,
):
await TipButton.execute(interaction, member, amount, memo)

Expand Down
File renamed without changes.
6 changes: 3 additions & 3 deletions bot/settings.py → discordbot/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ class Config:


class StandaloneSettings(DiscordSettings):
lnbits_url: HttpUrl
lnbits_admin_key: str
lnbits_url: Optional[HttpUrl] = None
lnbits_admin_key: Optional[str] = None
discord_bot_token: Optional[str] = None
data_folder: Optional[Path] = "/data"
data_folder: Optional[Path] = "./data"


discord_settings = DiscordSettings()
52 changes: 26 additions & 26 deletions bot/ui.py → discordbot/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,39 +40,39 @@ async def execute(
interaction: LnbitsInteraction,
member: discord.Member,
amount: int,
memo: str = None,
memo: Optional[str] = None,
):
try:
await interaction.client.api.send_payment(
interaction.user, member, amount, memo
if interaction.user == member:
await interaction.response.send_message(
ephemeral=True, content="You cant pay yourself"
)
except HTTPStatusError as e:
await interaction.response.send_message(content=e.response.content)
return
else:
try:
await interaction.client.api.send_payment(
interaction.user, member, amount, memo
)
except HTTPStatusError as e:
await interaction.response.send_message(content=e.response.content)
return

embed = discord.Embed(
title="Tip",
color=discord.Color.yellow(),
description=f"{interaction.user.mention} just sent **{get_amount_str(amount)}** to {member.mention}",
)
if memo:
embed.add_field(name="Memo", value=memo)
embed = discord.Embed(
title="Tip",
color=discord.Color.yellow(),
description=f"{interaction.user.mention} just sent **{get_amount_str(amount)}** to {member.mention}",
)
if memo:
embed.add_field(name="Memo", value=memo)

await interaction.response.send_message(
embed=embed, view=discord.ui.View().add_item(cls(amount, member))
)
await interaction.response.send_message(
embed=embed, view=discord.ui.View().add_item(cls(amount, member))
)

await interaction.client.try_send_payment_notification(
interaction, interaction.user, member, amount, memo
)
await interaction.client.try_send_payment_notification(
interaction, interaction.user, member, amount, memo
)

async def callback(self, interaction: LnbitsInteraction):
if interaction.user == self.receiver:
await interaction.response.send_message(
ephemeral=True, content="You cant pay yourself"
)
else:
await self.execute(interaction, self.receiver, self.amount)
await self.execute(interaction, self.receiver, self.amount)


class PayButton(discord.ui.Button):
Expand Down
File renamed without changes.
79 changes: 12 additions & 67 deletions migrations.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from lnbits.db import SQLITE, Database
import json

from lnbits.db import Database


async def m001_initial(db):
Expand Down Expand Up @@ -37,73 +39,16 @@ async def m002_major_overhaul(db: Database):
# Initial settings table
await db.execute(
"""
CREATE TABLE discordbot.settings (
CREATE TABLE IF NOT EXISTS discordbot.bots (
admin TEXT PRIMARY KEY,
CONSTRAINT admin_account_id
FOREIGN KEY(admin)
REFERENCES accounts(id)
ON DELETE cascade,
bot_token TEXT NOT NULL UNIQUE
token TEXT NOT NULL UNIQUE,
standalone BOOLEAN NOT NULL DEFAULT TRUE,
name TEXT NULL,
avatar_url TEXT NULL,
CONSTRAINT admin_account_id
FOREIGN KEY(admin)
REFERENCES accounts(id)
ON DELETE cascade
);
"""
)
# Migrate old data
if db.type == SQLITE:
await db.execute(
"""
INSERT INTO usermanager.users (id, name, admin, extra)
SELECT id, name, admin, json_object('discord_id', discord_id) FROM discordbot.users
"""
)

else:
await db.execute(
"""
INSERT INTO usermanager.users (id, name, admin, extra)
SELECT id, name, admin, json_build_object('discord_id', discord_id) FROM discordbot.users
"""
)

await db.execute(
"""
INSERT INTO usermanager.wallets (id, admin, name, "user", adminkey, inkey)
SELECT * FROM discordbot.wallets
"""
)

# Drop old tables
await db.execute("DROP TABLE discordbot.users")
await db.execute("DROP TABLE discordbot.wallets")


async def m003_add_start_to_settings(db: Database):
await db.execute(
"""
ALTER TABLE discordbot.settings
ADD COLUMN standalone BOOLEAN NOT NULL DEFAULT TRUE
"""
)
await db.execute(
"""
ALTER TABLE discordbot.settings
ADD COLUMN name TEXT NULL
"""
)
await db.execute(
"""
ALTER TABLE discordbot.settings
ADD COLUMN avatar_url TEXT NULL
"""
)
await db.execute(
"""
ALTER TABLE discordbot.settings
RENAME COLUMN bot_token TO token
"""
)
# rename settings to bots
await db.execute(
"""
ALTER TABLE discordbot.settings RENAME TO bots;
"""
)
Loading