Skip to content

Commit

Permalink
1.1.8v
Browse files Browse the repository at this point in the history
Merge pull request #44 from suchencjusz/1.1.8v
  • Loading branch information
suchencjusz authored Jan 4, 2025
2 parents 1c8f3f7 + c805b96 commit 6b8c23b
Show file tree
Hide file tree
Showing 14 changed files with 340 additions and 15 deletions.
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ services:

environment:
- DISCORD_TOKEN= your discord bot token goes here
- LOG_LEVEL=DEBUG

networks:
- db
Expand Down
25 changes: 23 additions & 2 deletions src/filman_discord/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
# FROM python:3.11-alpine

# RUN apk update && apk add --no-cache \
# gcc \
# musl-dev \
# libffi-dev \
# openssl-dev \
# mariadb-dev \
# build-base

# WORKDIR /src/filman_discord

# COPY requirements.txt requirements.txt
# RUN pip install --no-cache-dir -r requirements.txt

# COPY /src/filman_discord .

# CMD [ "python3", "-OO", "main.py"]

FROM python:3.11-alpine

RUN apk update && apk add --no-cache \
Expand All @@ -13,6 +32,8 @@ WORKDIR /src/filman_discord
COPY requirements.txt requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

COPY /src/filman_discord .
COPY src /src

ENV PYTHONPATH=/src

CMD [ "python3", "-OO", "main.py"]
CMD ["python3", "-OO", "main.py"]
89 changes: 85 additions & 4 deletions src/filman_discord/endpoints/filmweb.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import logging
from datetime import datetime

import hikari
import lightbulb

from filman_discord.utils.filmweb_w2s_logic import MediaType, process_media

tracker_plugin = lightbulb.Plugin("Filmweb")


Expand Down Expand Up @@ -329,9 +332,87 @@ async def cancel_subcommand(ctx: lightbulb.SlashContext) -> None:
return


def load(bot: lightbulb.BotApp) -> None:
bot.add_plugin(tracker_plugin)
# todo: draw within common friends list
@tracker_group.child
@lightbulb.option("user5", "Piąty użytkownik", type=hikari.User, required=False)
@lightbulb.option("user4", "Czwarty użytkownik", type=hikari.User, required=False)
@lightbulb.option("user3", "Trzeci użytkownik", type=hikari.User, required=False)
@lightbulb.option("user2", "Drugi użytkownik", type=hikari.User, required=False)
@lightbulb.option("user1", "Pierwszy użytkownik (wymagany)", type=hikari.User, required=True)
@lightbulb.option("common", "Losuj tylko z wspólnych elemntów list", type=bool, required=False, autocomplete=False)
@lightbulb.option(
"typ", "Wybierz co chcesz losować: film / serial", type=str, required=True, choices=["film", "serial"], default="film"
)
@lightbulb.command("w2s", "Wylosuj film lub serial z listy 'chcę obejrzeć' dla użytkownika/użytkowników", pass_options=True)
@lightbulb.implements(lightbulb.SlashSubCommand)
async def w2s_subcommand(
ctx: lightbulb.SlashContext,
typ: str,
user1: hikari.User,
user2: hikari.User = None,
user3: hikari.User = None,
user4: hikari.User = None,
user5: hikari.User = None,
common: bool = False,
) -> None:
users = [user1, user2, user3, user4, user5]
mentioned_users = [user.mention for user in users if user is not None]

for user in users:
if user is not None:
if user.is_bot:
embed = hikari.Embed(
title="Nie możesz losować filmów dla bota, zostaw go w spokoju!",
colour=0xFF4400,
timestamp=datetime.now().astimezone(),
)
embed.set_footer(
text=f"Requested by {ctx.author}",
icon=ctx.author.display_avatar_url,
)
return await ctx.respond(embed)

if len(mentioned_users) == 0:
embed = hikari.Embed(
title="Nie podano użytkowników!",
colour=0xFF4400,
timestamp=datetime.now().astimezone(),
)
embed.set_footer(
text=f"Requested by {ctx.author}",
icon=ctx.author.display_avatar_url,
)
return await ctx.respond(embed)

# @tracker_group.child
# @lightbulb.command("list", "lista powiadomień", pass_options=True)
if len(mentioned_users) == 1 and common is True:
embed = hikari.Embed(
title="Nie możesz losować wspólnych filmów dla jednej osoby, przecież to nie ma sensu xd",
colour=0xFF4400,
timestamp=datetime.now().astimezone(),
)
embed.set_footer(
text=f"Requested by {ctx.author}",
icon=ctx.author.display_avatar_url,
)
return await ctx.respond(embed)

response = f"Oznaczono {len(mentioned_users)} użytkowników:\n" + "\n".join(mentioned_users)

embed = hikari.Embed(
title=f"Losowanie `{typ}`...",
description=response,
colour=0xFFC200,
timestamp=datetime.now().astimezone(),
)

await ctx.respond(embed)

logging.debug(f"Processing users: {users}")

media_type = MediaType.FILM if typ == "film" else MediaType.SERIAL

await ctx.edit_last_response(content=await process_media(users, common, media_type=media_type), embed=None)


def load(bot: lightbulb.BotApp) -> None:
bot.add_plugin(tracker_plugin)
10 changes: 10 additions & 0 deletions src/filman_discord/endpoints/help.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,21 @@ async def filmweb_subcommand(ctx: lightbulb.SlashContext) -> None:
value="Anuluje wysyłanie powiadomień na danym serwerze",
)

embed.add_field(
name="`/filmweb stop_everything`",
value="Anuluje wysyłanie powiadomień na wszystkich serwerach - usuwa dane użytkownika z bazy danych",
)

embed.add_field(
name="`/filmweb here`",
value="Dopisuje użytkownika do listy powiadomień na danym serwerze",
)

embed.add_field(
name="`/filmweb w2s`",
value="Losuje film/serial z list/y użytkowników \n opcja common losuje film z wspólnych elementów wszystkich użytkowników",
)

embed.set_footer(
text=f"Requested by {ctx.author}",
icon=ctx.author.display_avatar_url,
Expand Down
35 changes: 32 additions & 3 deletions src/filman_discord/endpoints/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,22 @@
info_plugin = lightbulb.Plugin("Info")


@info_plugin.command
@info_plugin.command()
@lightbulb.command("info", "informacje o bocie")
@lightbulb.implements(lightbulb.SlashCommand)
async def info_command(ctx: lightbulb.SlashContext) -> None:
async def info_group(ctx: lightbulb.SlashContext) -> None:
pass


@info_group.child
@lightbulb.command("basic", "podstawowe informacje o bocie")
@lightbulb.implements(lightbulb.SlashCommand)
async def info_basic_command(ctx: lightbulb.SlashContext) -> None:
embed = hikari.Embed(title="`Filman`", colour=0xFFC200, timestamp=datetime.now().astimezone())

embed.add_field(
name="Wersja i ostatnia aktualizacja",
value="`1.1.7Av` - `2024-12-04`",
value="`1.1.8v` - `2025-01-04`",
)

embed.add_field(
Expand All @@ -35,5 +42,27 @@ async def info_command(ctx: lightbulb.SlashContext) -> None:
await ctx.respond(embed)


@info_group.child
@lightbulb.command("database", "informacje o bazie danych")
@lightbulb.implements(lightbulb.SlashCommand)
async def info_database_command(ctx: lightbulb.SlashContext) -> None:
async with ctx.bot.d.client_session.get("http://filman_server:8000/utils/database_info") as response:
data = await response.json()

embed = hikari.Embed(title="`Filman`", colour=0xFFC200, timestamp=datetime.now().astimezone())

embed.add_field(
name=f"W bazie danych znajduje się {data['users_count']} użytkowników \n którzy wszyscy obejrzeli {data['filmweb_watched_movies']} filmów \n oraz {data['filmweb_watched_series']} seriali, \n a także jest zarejestrowanych {data['discord_guilds']} serwerów.",
value="",
)

embed.set_footer(
text=f"Requested by {ctx.author}",
icon=ctx.author.display_avatar_url,
)

await ctx.respond(embed)


def load(bot: lightbulb.BotApp) -> None:
bot.add_plugin(info_plugin)
92 changes: 92 additions & 0 deletions src/filman_discord/utils/filmweb_w2s_logic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import logging
import random
from enum import Enum

import requests

logger = logging.getLogger(__name__)


class MediaType(Enum):
FILM = "film"
SERIAL = "serial"


async def fetch_filmweb_id(user: any) -> str | None:
logger.debug(f"Fetching Filmweb ID for user: {user.id}")
response = requests.get(f"http://filman_server:8000/filmweb/user/mapping/get", params={"discord_id": user.id})
if response.status_code == 200:
filmweb_id = response.json().get("filmweb_id")
if filmweb_id:
logger.debug(f"Found Filmweb ID: {filmweb_id} for user: {user.id}")
return filmweb_id
else:
logger.warning(f"No Filmweb ID found for user: {user.id}")
else:
logger.error(f"Error fetching Filmweb ID for user: {user.id}, status: {response.status_code}")
return None


async def fetch_media_to_watch(filmweb_id: str, media_type: MediaType) -> dict[str, any] | None:
logger.debug(f"Fetching list of media to watch for Filmweb ID: {filmweb_id}")
response = requests.get(f"https://www.filmweb.pl/api/v1/user/{filmweb_id}/want2see/{media_type.value}", timeout=5)
if response.status_code == 200:
return response.json()
else:
logger.error(f"Error fetching list of media for Filmweb ID: {filmweb_id}, status: {response.status_code}")
return None


async def process_media(users: any, draw_common_media: bool = False, media_type: MediaType = MediaType.FILM) -> str:
mentioned_users = [user.mention for user in users if user is not None]
logger.debug(f"Users: {mentioned_users}")

filmweb_ids = {await fetch_filmweb_id(user): user for user in users if user is not None}
filmweb_ids = {k: v for k, v in filmweb_ids.items() if k is not None}

media_to_watch: dict[str, dict[str, any]] = {}
for filmweb_id, user in filmweb_ids.items():
media_entities = await fetch_media_to_watch(filmweb_id, media_type)
if media_entities:
for media in media_entities:
media_id = media.get("entity")
media_url = f"https://www.filmweb.pl/{media_type.value}/x-1-{media_id}"
if media_id not in media_to_watch:
media_to_watch[media_id] = {"url": media_url, "users": set()}
media_to_watch[media_id]["users"].add(user.id)
logger.debug(f"Added {media_type.value} to list: {media_url} from user: {user.id}")

common_media = [media for media in media_to_watch.values() if len(media["users"]) > 1]

response_url = ""
response_mentions = ""

if draw_common_media:
if common_media:
common_media_item = random.choice(common_media)
user_mentions = ", ".join([f"<@{user_id}>" for user_id in common_media_item["users"]])
response_url = common_media_item["url"]
response_mentions = f"z listy {user_mentions}"
else:
if media_type == MediaType.FILM:
response_url = "Brak wspólnych filmów."
else:
response_url = "Brak wspólnych seriali."
response_mentions = ""
else:
uncommon_media = [media for media in media_to_watch.values() if len(media["users"]) == 1]
if uncommon_media:
uncommon_media_item = random.choice(uncommon_media)
user_mentions = ", ".join([f"<@{user_id}>" for user_id in uncommon_media_item["users"]])
response_url = uncommon_media_item["url"]
response_mentions = f"z listy {user_mentions}"
else:
if media_type == MediaType.FILM:
response_url = "Brak filmów do obejrzenia."
else:
response_url = "Brak seriali do obejrzenia."
response_mentions = ""

response = f"{response_url} {response_mentions}"

return response
8 changes: 8 additions & 0 deletions src/filman_server/database/crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,10 @@ def delete_filmweb_user_watched_movies(
return True


def get_filmweb_watched_movies_all(db: Session) -> list[models.FilmWebUserWatchedMovie]:
return db.query(models.FilmWebUserWatchedMovie).all()


# SERIES WATCHED
def create_filmweb_user_watched_series(db: Session, user_watched_series: schemas.FilmWebUserWatchedSeriesCreate):
watched_series = get_series_filmweb_id(db, user_watched_series.id_media)
Expand Down Expand Up @@ -607,6 +611,10 @@ def delete_filmweb_user_watched_series(
return True


def get_filmweb_watched_series_all(db: Session) -> list[models.FilmWebUserWatchedSeries]:
return db.query(models.FilmWebUserWatchedSeries).all()


#
# TASKS
#
Expand Down
12 changes: 12 additions & 0 deletions src/filman_server/database/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,3 +224,15 @@ class TaskCreate(BaseModel):
task_started: Optional[datetime] = None
task_finished: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)


#
# UTILS
#


class DatabaseInfo(BaseModel):
users_count: int
filmweb_watched_movies: int
filmweb_watched_series: int
discord_guilds: int
3 changes: 2 additions & 1 deletion src/filman_server/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from filman_server.cron import Cron
from filman_server.database import models
from filman_server.database.db import engine
from filman_server.routes import discord, filmweb, tasks, users
from filman_server.routes import discord, filmweb, tasks, users, utils

LOG_LEVEL = os.environ.get("LOG_LEVEL", "INFO")
logging.basicConfig(
Expand Down Expand Up @@ -42,6 +42,7 @@
app.include_router(discord.discord_router)
app.include_router(filmweb.filmweb_router)
app.include_router(tasks.tasks_router)
app.include_router(utils.utils_router)


@app.get("/")
Expand Down
3 changes: 1 addition & 2 deletions src/filman_server/routes/filmweb.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import logging
import os
from typing import List
from urllib.parse import quote

import requests
from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session

from urllib.parse import quote

from filman_server.database import crud, schemas
from filman_server.database.db import get_db

Expand Down
2 changes: 1 addition & 1 deletion src/filman_server/routes/tasks.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# for what purpose rabbitmq exists
# if you can build your task broker and learn something :~~~~D
# if you can build your task broker and learn something :~~~~D (not doing it)

import logging
import os
Expand Down
Loading

0 comments on commit 6b8c23b

Please sign in to comment.