Save sync token, check if videos are music before saving.
Signed-off-by: Abdulkadir Furkan Şanlı <me@abdulocra.cy>
This commit is contained in:
parent
7aba9580b6
commit
3d4483539b
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,3 +5,4 @@ venv
|
||||
*.pickle
|
||||
*.json
|
||||
*.swp
|
||||
sync_token
|
||||
|
97
main.py
97
main.py
@ -1,17 +1,18 @@
|
||||
#!/usr/bin/env python3
|
||||
"""parkerbot: Matrix bot to generate YouTube (music) playlists from links sent to a channel."""
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import pickle
|
||||
import re
|
||||
import sqlite3
|
||||
import asyncio
|
||||
import pickle
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from dotenv import load_dotenv
|
||||
from google.auth.transport.requests import Request
|
||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
from googleapiclient.discovery import build
|
||||
from google.auth.transport.requests import Request
|
||||
from nio import AsyncClient, MatrixRoom, RoomMessageText
|
||||
from nio import AsyncClient, ClientConfig, RoomMessageText, SyncResponse
|
||||
|
||||
load_dotenv()
|
||||
DB_PATH = os.getenv("DB_PATH")
|
||||
@ -28,7 +29,8 @@ conn = sqlite3.connect(DB_PATH)
|
||||
cursor = conn.cursor()
|
||||
|
||||
|
||||
def create_tables():
|
||||
def define_tables():
|
||||
"""Define tables for use with program."""
|
||||
with conn:
|
||||
cursor.execute(
|
||||
"""CREATE TABLE IF NOT EXISTS messages (
|
||||
@ -57,6 +59,7 @@ def create_tables():
|
||||
|
||||
|
||||
def get_authenticated_service():
|
||||
"""Get an authentivated YouTube service."""
|
||||
credentials = None
|
||||
# The file token.pickle stores the user's access and refresh tokens.
|
||||
if os.path.exists("token.pickle"):
|
||||
@ -81,11 +84,13 @@ def get_authenticated_service():
|
||||
|
||||
|
||||
def get_monday_date():
|
||||
"""Get Monday of current week. Weeks start on Monday."""
|
||||
today = datetime.now()
|
||||
return today - timedelta(days=today.weekday())
|
||||
|
||||
|
||||
def create_playlist(youtube, title):
|
||||
def make_playlist(youtube, title):
|
||||
"""Make a playlist with given title."""
|
||||
response = (
|
||||
youtube.playlists()
|
||||
.insert(
|
||||
@ -93,7 +98,7 @@ def create_playlist(youtube, title):
|
||||
body={
|
||||
"snippet": {
|
||||
"title": title,
|
||||
"description": "Weekly playlist created by ParkerBot",
|
||||
"description": "Weekly playlist generated by ParkerBot",
|
||||
},
|
||||
"status": {"privacyStatus": "public"},
|
||||
},
|
||||
@ -104,7 +109,8 @@ def create_playlist(youtube, title):
|
||||
return response["id"]
|
||||
|
||||
|
||||
def get_or_create_playlist(youtube, monday_date):
|
||||
def get_or_make_playlist(youtube, monday_date):
|
||||
"""Get ID of playlist for given Monday's week, make if doesn't exist."""
|
||||
playlist_title = f"{PLAYLIST_TITLE} {monday_date.strftime('%Y-%m-%d')}"
|
||||
|
||||
# Check if playlist exists in the database
|
||||
@ -115,8 +121,8 @@ def get_or_create_playlist(youtube, monday_date):
|
||||
if row:
|
||||
return row[0] # Playlist already exists
|
||||
|
||||
# If not, create a new playlist on YouTube and save it in the database
|
||||
playlist_id = create_playlist(youtube, playlist_title)
|
||||
# If not, make a new playlist on YouTube and save it in the database
|
||||
playlist_id = make_playlist(youtube, playlist_title)
|
||||
with conn:
|
||||
cursor.execute(
|
||||
"INSERT INTO playlists (title, playlist_id, creation_date) VALUES (?, ?, ?)",
|
||||
@ -127,6 +133,7 @@ def get_or_create_playlist(youtube, monday_date):
|
||||
|
||||
|
||||
def add_video_to_playlist(youtube, playlist_id, video_id):
|
||||
"""Add video to playlist."""
|
||||
youtube.playlistItems().insert(
|
||||
part="snippet",
|
||||
body={
|
||||
@ -138,32 +145,39 @@ def add_video_to_playlist(youtube, playlist_id, video_id):
|
||||
).execute()
|
||||
|
||||
|
||||
async def message_callback(room, event):
|
||||
def is_music(youtube, video_id):
|
||||
"""Check whether a YouTube video is music."""
|
||||
video_details = youtube.videos().list(id=video_id, part="snippet").execute()
|
||||
|
||||
# Check if the video category is Music (typically category ID 10)
|
||||
return video_details["items"][0]["snippet"]["categoryId"] == "10"
|
||||
|
||||
|
||||
async def message_callback(client, room, event):
|
||||
"""Event handler for received messages."""
|
||||
youtube_link_pattern = r"(https?://(?:www\.|music\.)?youtube\.com/(?!playlist\?list=)watch\?v=[\w-]+|https?://youtu\.be/[\w-]+)"
|
||||
if event.sender != MATRIX_USER:
|
||||
sender = event.sender
|
||||
message_body = event.body
|
||||
sender = event.sender
|
||||
if sender != MATRIX_USER:
|
||||
body = event.body
|
||||
timestamp = event.server_timestamp
|
||||
room_id = room.room_id
|
||||
monday_date = get_monday_date()
|
||||
youtube = get_authenticated_service()
|
||||
playlist_id = get_or_create_playlist(youtube, monday_date)
|
||||
youtube_links = re.findall(youtube_link_pattern, message_body)
|
||||
playlist_id = get_or_make_playlist(youtube, monday_date)
|
||||
youtube_links = re.findall(youtube_link_pattern, body)
|
||||
|
||||
if message_body == "!pow":
|
||||
if body == "!pow":
|
||||
playlist_link = f"https://www.youtube.com/playlist?list={playlist_id}"
|
||||
reply_msg = f"{sender}, here's the playlist of the week: {playlist_link}"
|
||||
await client.room_send(
|
||||
room_id=room_id,
|
||||
message_type="m.room.message",
|
||||
content={
|
||||
"msgtype": "m.text",
|
||||
"body": reply_msg
|
||||
}
|
||||
content={"msgtype": "m.text", "body": reply_msg},
|
||||
)
|
||||
|
||||
if youtube_links:
|
||||
timestamp = event.server_timestamp
|
||||
for link in youtube_links:
|
||||
for link in youtube_links:
|
||||
video_id = link.split("v=")[-1]
|
||||
if is_music(youtube, video_id):
|
||||
try:
|
||||
cursor.execute(
|
||||
"INSERT INTO messages (sender, message, timestamp) VALUES (?, ?, ?)",
|
||||
@ -177,9 +191,6 @@ async def message_callback(room, event):
|
||||
else:
|
||||
raise e
|
||||
|
||||
for link in youtube_links:
|
||||
video_id = link.split("v=")[-1]
|
||||
|
||||
# Check if the link is already added to any playlist
|
||||
cursor.execute("SELECT id FROM messages WHERE message = ?", (link,))
|
||||
message_row = cursor.fetchone()
|
||||
@ -204,15 +215,37 @@ async def message_callback(room, event):
|
||||
print(f"Added track to playlist: {link}")
|
||||
|
||||
|
||||
async def main():
|
||||
create_tables()
|
||||
global client
|
||||
async def sync_callback(response):
|
||||
# Save the sync token to a file or handle it as needed
|
||||
with open("sync_token", "w") as f:
|
||||
f.write(response.next_batch)
|
||||
|
||||
|
||||
def load_sync_token():
|
||||
try:
|
||||
with open("sync_token", "r") as file:
|
||||
return file.read().strip()
|
||||
except FileNotFoundError:
|
||||
return None
|
||||
|
||||
|
||||
async def get_client():
|
||||
client = AsyncClient(MATRIX_SERVER, MATRIX_USER)
|
||||
client.add_event_callback(message_callback, RoomMessageText)
|
||||
client.add_event_callback(
|
||||
lambda room, event: message_callback(client, room, event), RoomMessageText
|
||||
)
|
||||
client.add_response_callback(sync_callback, SyncResponse)
|
||||
print(await client.login(MATRIX_PASSWORD))
|
||||
await client.join(MATRIX_ROOM)
|
||||
await client.sync_forever(timeout=10000) # milliseconds
|
||||
return client
|
||||
|
||||
|
||||
async def main():
|
||||
"""Get DB and Matrix client ready, and start syncing."""
|
||||
define_tables()
|
||||
client = await get_client()
|
||||
sync_token = load_sync_token()
|
||||
await client.sync_forever(30000, full_state=True, since=sync_token)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
Loading…
Reference in New Issue
Block a user