Add random status generator

This commit is contained in:
sHa
2024-04-07 01:05:53 +03:00
parent 051b1b4e40
commit 13475a7db4
9 changed files with 157 additions and 55 deletions

3
.gitignore vendored
View File

@@ -2,4 +2,5 @@
.venv .venv
__pycache__ __pycache__
.mypy_cache .mypy_cache
.pytest_cache .pytest_cache
.DS_Store

View File

@@ -1,4 +1,5 @@
from appscript import app # type: ignore from appscript import app # type: ignore
class AppleMusic: class AppleMusic:
def __init__(self): def __init__(self):
@@ -7,11 +8,14 @@ class AppleMusic:
def get_current_track_info(self) -> tuple: def get_current_track_info(self) -> tuple:
try: try:
current_track = self.music_app.current_track.get() current_track = self.music_app.current_track.get()
current_position = self.music_app.player_position.get()
track_duration = current_track.duration.get()
return ( return (
current_track.name.get(), current_track.name.get(),
current_track.artist.get(), current_track.artist.get(),
current_track.duration.get(), track_duration,
current_position,
) )
except Exception as e: except Exception as e:
print(f"Failed to get current track info: {e}") print(f"Failed to get current track info: {e}")
return None, None, None return None, None, None, None

27
connectors/random.py Normal file
View File

@@ -0,0 +1,27 @@
from faker import Faker
import random
import emoji
class Random:
def __init__(self):
self.faker = Faker()
def get_random_activity(self) -> tuple:
activity = self.faker.bs().capitalize() # Generate a random activity
emoji = self.get_random_emoji_name() # Get a random emoji
duration = random.randint(5, 60) # Duration in minutes
return activity, emoji, duration
def get_random_emoji_name(self) -> str:
emoji_names = list(emoji.get_aliases_unicode_dict())
single_char_emoji_names = [
name for name in emoji_names if len(emoji.emojize(name)) == 1
]
random_emoji_name = random.choice(single_char_emoji_names)
return random_emoji_name
def get_random_emoji(self) -> str:
random_emoji = emoji.emojize(self.get_random_emoji_name())
return random_emoji

View File

@@ -1,17 +1,30 @@
import osascript # type: ignore import osascript # type: ignore
class Spotify: class Spotify:
def get_current_track_info(self) -> tuple: def get_current_track_info(self) -> tuple:
try: try:
name_code = 'tell application "Spotify" to name of current track as string' name_code = 'tell application "Spotify" to name of current track as string'
artist_code = 'tell application "Spotify" to artist of current track as string' artist_code = (
duration_code = 'tell application "Spotify" to duration of current track as string' 'tell application "Spotify" to artist of current track as string'
)
duration_code = (
'tell application "Spotify" to duration of current track as string'
)
elapsed_time_code = (
'tell application "Spotify" to player position as string'
)
name = osascript.osascript(name_code)[1] name = osascript.osascript(name_code)[1]
artist = osascript.osascript(artist_code)[1] artist = osascript.osascript(artist_code)[1]
duration = int(osascript.osascript(duration_code)[1]) / 1000 # Convert duration from ms to s duration = (
int(osascript.osascript(duration_code)[1]) / 1000
) # Convert duration from ms to s
elapsed_time = (
float(osascript.osascript(elapsed_time_code)[1].replace(',','.'))
) # Elapsed time in seconds
return name, artist, duration return name, artist, duration, elapsed_time
except Exception as e: except Exception as e:
print(f"Failed to get current track info: {e}") print(f"Failed to get current track info: {e}")
return None, None, None return None, None, None, None

View File

@@ -2,6 +2,7 @@ from datetime import datetime, timedelta, timezone
from dotenv import load_dotenv from dotenv import load_dotenv
import os import os
import requests import requests
import emoji
load_dotenv() load_dotenv()
@@ -17,23 +18,28 @@ class Mattermost:
"Content-Type": "application/json", "Content-Type": "application/json",
} }
def set_status(self, status, emoji, expires_at=None): def set_status(self, status, emoji_name, expires_at=None):
emoji_icon = emoji.emojize(f":{emoji_name}:", language="alias")
print(f"Setting Mattermost status to {emoji_icon} {status} until {expires_at}")
data = { data = {
"emoji": emoji, "emoji": emoji_name,
"text": status, "text": status,
"expires_at": expires_at, "expires_at": expires_at.isoformat() if expires_at else None,
} }
response = requests.put(self.url, headers=self.headers, json=data) response = requests.put(self.url, headers=self.headers, json=data)
if response.status_code != 200: if response.status_code != 200:
print(f"Failed to set Mattermost status: {response.content}") print(f"Failed to set Mattermost status: {response.content}")
raise Exception(f"Failed to set Mattermost status: {response.content}") raise Exception(f"Failed to set Mattermost status: {response.content}")
def set_now_playing(self, track, artist, duration): def clear_status(self):
expires_at = (datetime.now(timezone.utc) + timedelta(seconds=duration)).astimezone() self.set_status("", "", None)
status = f"{track} - {artist}"
print(f"Setting Mattermost status to {status} until {expires_at}")
self.set_status(status, "headphones", expires_at.isoformat())
def set_now_playing(self, track, artist, duration):
expires_at = (
datetime.now(timezone.utc) + timedelta(seconds=duration)
).astimezone()
status = f"{track} - {artist}"
self.set_status(status, "headphones", expires_at)
def get_status(self): def get_status(self):
response = requests.get(self.url, headers=self.headers) response = requests.get(self.url, headers=self.headers)

View File

@@ -1,37 +1,65 @@
from datetime import datetime from datetime import datetime, timedelta, timezone
import time
from music import Music
from mattermost import Mattermost
from dotenv import load_dotenv from dotenv import load_dotenv
from mattermost import Mattermost
from processors.music import MusicProcessor
from processors.text import TextProcessor
import argparse
import time
load_dotenv() load_dotenv()
SLEEP_TIME = 3 SLEEP_TIME = 3
def playing_now() -> tuple:
music = Music() def get_status(source: str | None = None) -> dict:
return music.get_current_track_info() if source == "random":
activity, emoji, duration = TextProcessor(source=source).get_satus()
return {
"status": activity,
"emoji": emoji.replace(":", ""),
"expires_at": datetime.now(timezone.utc) + timedelta(minutes=duration),
"ending_time": duration * 60,
}
if source == "music":
track, artist, duration, elapsed_time = (
MusicProcessor().get_current_track_info()
)
if track and artist and duration:
now = datetime.now(timezone.utc)
print(f"{now} 🎧 {track} - {artist}")
expires_at = (
now + timedelta(seconds=duration) - timedelta(seconds=elapsed_time)
).astimezone()
return {
"status": f"{track} - {artist}",
"emoji": "headphones",
"expires_at": expires_at,
"ending_time": duration - elapsed_time,
}
return {
"status": None,
"emoji": None,
"expires_at": None,
"ending_time": None,
}
def set_now_playing(name, artist, duration): def send_user_status(status, emoji, expires_at=None, **kwargs):
now = datetime.now().strftime("%H:%M:%S") Mattermost().set_status(status, emoji, expires_at=expires_at)
duration = int(duration) if duration else 0
print(f"{now} 🎧 {name} - {artist} ⏱️ {duration} seconds")
if name and artist and duration:
Mattermost().set_now_playing(name, artist, duration)
def main(): def main(source: str | None = "music"):
name_curr, artist_curr, duration_curr = playing_now() status_curr = {"status": None}
set_now_playing(name_curr, artist_curr, duration_curr)
while True: while True:
name, artist, duration = playing_now() status = get_status(source)
if name != name_curr: if status.get("status") != status_curr.get("status"):
set_now_playing(name, artist, duration) send_user_status(**status)
name_curr = name status_curr = status
time.sleep(SLEEP_TIME) time.sleep(status.get("ending_time") or SLEEP_TIME)
if __name__ == "__main__": if __name__ == "__main__":
main() parser = argparse.ArgumentParser()
parser.add_argument("--source", help="source to use for connector", default="music")
args = parser.parse_args()
main(args.source)

View File

@@ -4,26 +4,29 @@ from connectors.spotify import Spotify
import os import os
from dotenv import load_dotenv from dotenv import load_dotenv
class Music:
class MusicProcessor:
def __init__(self): def __init__(self):
load_dotenv() load_dotenv()
self.music_app = os.getenv('MUSIC_APP', 'autodetect') self.music_app = os.getenv("MUSIC_APP", "autodetect")
if self.music_app == 'autodetect': if self.music_app == "autodetect":
self.music_app = self.get_current_music_player() self.music_app = self.get_current_music_player()
self.connector = self.get_connector() self.connector: Spotify | AppleMusic | None = self.get_connector()
def get_connector(self): def get_connector(self) -> Spotify | AppleMusic | None:
if self.music_app == 'spotify': match self.music_app:
return Spotify() case "spotify":
elif self.music_app == 'apple_music': return Spotify()
return AppleMusic() case "apple_music":
else: return AppleMusic()
raise ValueError(f'Invalid music app: {self.music_app}') case _:
print("Active music player not found")
return None
def get_current_track_info(self) -> tuple: def get_current_track_info(self) -> tuple:
if self.connector: if self.connector:
return self.connector.get_current_track_info() return self.connector.get_current_track_info()
return None, None, None return None, None, None, None
@staticmethod @staticmethod
def get_current_music_player(): def get_current_music_player():
@@ -42,9 +45,9 @@ class Music:
.strip() .strip()
) )
if spotify_status == "true": if spotify_status == "true":
player = "Spotify" player = "spotify"
elif apple_music_status == "true": elif apple_music_status == "true":
player = "Apple Music" player = "apple_music"
else: else:
player = None player = None

18
processors/text.py Normal file
View File

@@ -0,0 +1,18 @@
from connectors.random import Random
class TextProcessor:
def __init__(self, source: str | None = None):
self.source: str | None = source
self.connector = self.get_connector()
def get_connector(self):
if self.source == "random":
return Random()
else:
raise ValueError("Invalid source")
def get_satus(self) -> tuple:
if self.connector:
return self.connector.get_random_activity()
return None, None, None

View File

@@ -2,3 +2,5 @@ python-dotenv
requests requests
appscript appscript
osascript osascript
faker
emoji