Add Last.fm integration plugin with YouTube link support
- Added Last.fm plugin for tracking currently playing music with !register and !np commands - Added YouTube API integration to provide links to currently playing songs - Updated help.py and README.md with documentation for the new plugin - Plugin stores user associations in SQLite database - Added YouTube API key to .env file for YouTube search functionality - Supports displaying track name in bold text with album information when available - Added YouTube link to songs when available
This commit is contained in:
@@ -292,6 +292,20 @@ Perform IP geolocation lookups with detailed geographic information.
|
|||||||
- ISP/Organization
|
- ISP/Organization
|
||||||
- Autonomous System Number (ASN)
|
- Autonomous System Number (ASN)
|
||||||
|
|
||||||
|
### 🎵 Last.fm Integration
|
||||||
|
|
||||||
|
**🎵 !register [username] and !np**
|
||||||
|
Register your Last.fm username and display currently playing tracks.
|
||||||
|
|
||||||
|
**Features:**
|
||||||
|
- Associate Matrix ID with Last.fm username
|
||||||
|
- Display currently playing tracks
|
||||||
|
- Uses SQLite database for user associations
|
||||||
|
|
||||||
|
**Commands:**
|
||||||
|
- `!register <username>` - Register your Last.fm username
|
||||||
|
- `!np` - Display currently playing track
|
||||||
|
|
||||||
### ExploitDB Plugin
|
### ExploitDB Plugin
|
||||||
|
|
||||||
A security plugin that searches Exploit-DB for vulnerabilities and exploits directly from Matrix.
|
A security plugin that searches Exploit-DB for vulnerabilities and exploits directly from Matrix.
|
||||||
|
|||||||
@@ -358,6 +358,32 @@ Search Exploit-DB for security vulnerabilities and exploits. Returns detailed in
|
|||||||
<em>Note: SSLv2/SSLv3 testing limited by Python security features</em></p>
|
<em>Note: SSLv2/SSLv3 testing limited by Python security features</em></p>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details><summary>🎵 <strong>Last.fm Integration</strong></summary>
|
||||||
|
<p>Register your Last.fm username and display currently playing tracks.</p>
|
||||||
|
<p><strong>Commands:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><code>!register <username></code> - Register your Last.fm username</li>
|
||||||
|
<li><code>!np</code> - Display your currently playing track from Last.fm</li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>Features:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>Associate your Matrix ID with your Last.fm username</li>
|
||||||
|
<li>Display currently playing tracks with artist and album information</li>
|
||||||
|
<li>Uses SQLite database to store user associations</li>
|
||||||
|
<li>Requires LASTFM_API_KEY environment variable</li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>Examples:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><code>!register your_lastfm_username</code> - Register your Last.fm username</li>
|
||||||
|
<li><code>!np</code> - Show your currently playing track</li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>Requirements:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li>LASTFM_API_KEY in .env file</li>
|
||||||
|
<li>Last.fm account at last.fm</li>
|
||||||
|
</ul>
|
||||||
|
</details>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<details><summary>📸 <strong>!sd [prompt]</strong></summary>
|
<details><summary>📸 <strong>!sd [prompt]</strong></summary>
|
||||||
|
|||||||
@@ -0,0 +1,242 @@
|
|||||||
|
"""
|
||||||
|
This plugin provides Last.fm integration for the bot.
|
||||||
|
It allows users to register their Last.fm username and display their currently playing track.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import sqlite3
|
||||||
|
import simplematrixbotlib as botlib
|
||||||
|
import aiosqlite
|
||||||
|
import json
|
||||||
|
import aiohttp
|
||||||
|
|
||||||
|
# Database file path
|
||||||
|
DB_PATH = "lastfm.db"
|
||||||
|
|
||||||
|
async def init_db():
|
||||||
|
"""Initialize the database with the required tables."""
|
||||||
|
async with aiosqlite.connect(DB_PATH) as db:
|
||||||
|
await db.execute("""
|
||||||
|
CREATE TABLE IF NOT EXISTS user_lastfm (
|
||||||
|
matrix_user TEXT PRIMARY KEY,
|
||||||
|
lastfm_user TEXT NOT NULL
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
await db.commit()
|
||||||
|
|
||||||
|
async def get_lastfm_username(matrix_user):
|
||||||
|
"""Get Last.fm username for a Matrix user."""
|
||||||
|
async with aiosqlite.connect(DB_PATH) as db:
|
||||||
|
async with db.execute("SELECT lastfm_user FROM user_lastfm WHERE matrix_user = ?", (matrix_user,)) as cursor:
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
return row[0] if row else None
|
||||||
|
|
||||||
|
async def set_lastfm_username(matrix_user, lastfm_user):
|
||||||
|
"""Associate a Last.fm username with a Matrix user."""
|
||||||
|
async with aiosqlite.connect(DB_PATH) as db:
|
||||||
|
# Check if user already exists
|
||||||
|
async with db.execute("SELECT lastfm_user FROM user_lastfm WHERE matrix_user = ?", (matrix_user,)) as cursor:
|
||||||
|
row = await cursor.fetchone()
|
||||||
|
|
||||||
|
if row:
|
||||||
|
# Update existing record
|
||||||
|
await db.execute("UPDATE user_lastfm SET lastfm_user = ? WHERE matrix_user = ?", (lastfm_user, matrix_user))
|
||||||
|
else:
|
||||||
|
# Insert new record
|
||||||
|
await db.execute("INSERT INTO user_lastfm (matrix_user, lastfm_user) VALUES (?, ?)", (matrix_user, lastfm_user))
|
||||||
|
|
||||||
|
await db.commit()
|
||||||
|
|
||||||
|
async def get_recent_track(lastfm_user):
|
||||||
|
"""
|
||||||
|
Get the recent tracks for a Last.fm user.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
lastfm_user (str): Last.fm username
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Track information or None if error
|
||||||
|
"""
|
||||||
|
api_key = os.getenv("LASTFM_API_KEY")
|
||||||
|
if not api_key:
|
||||||
|
logging.error("LASTFM_API_KEY not found in environment variables")
|
||||||
|
return None
|
||||||
|
|
||||||
|
url = "http://ws.audioscrobbler.com/2.0/"
|
||||||
|
params = {
|
||||||
|
"method": "user.getrecenttracks",
|
||||||
|
"user": lastfm_user,
|
||||||
|
"api_key": api_key,
|
||||||
|
"format": "json",
|
||||||
|
"limit": "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.get(url, params=params) as response:
|
||||||
|
if response.status == 200:
|
||||||
|
data = await response.json()
|
||||||
|
return data
|
||||||
|
else:
|
||||||
|
logging.error(f"Last.fm API returned status {response.status}")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error querying Last.fm API: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def get_youtube_link(artist, track_name):
|
||||||
|
"""
|
||||||
|
Search for a YouTube link for the given artist and track using YouTube Data API.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
artist (str): Artist name
|
||||||
|
track_name (str): Track name
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: YouTube video URL or None if not found
|
||||||
|
"""
|
||||||
|
youtube_api_key = os.getenv("YOUTUBE_API_KEY")
|
||||||
|
if not youtube_api_key:
|
||||||
|
return None
|
||||||
|
|
||||||
|
search_query = f"{artist} {track_name}"
|
||||||
|
url = "https://www.googleapis.com/youtube/v3/search"
|
||||||
|
params = {
|
||||||
|
"part": "snippet",
|
||||||
|
"q": search_query,
|
||||||
|
"type": "video",
|
||||||
|
"key": youtube_api_key,
|
||||||
|
"maxResults": "1"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
async with aiohttp.ClientSession() as session:
|
||||||
|
async with session.get(url, params=params) as response:
|
||||||
|
if response.status == 200:
|
||||||
|
data = await response.json()
|
||||||
|
items = data.get("items", [])
|
||||||
|
if items:
|
||||||
|
video_id = items[0].get("id", {}).get("videoId")
|
||||||
|
if video_id:
|
||||||
|
return f"https://www.youtube.com/watch?v={video_id}"
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"Error searching YouTube: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def format_recent_track(matrix_user, lastfm_user, data):
|
||||||
|
"""
|
||||||
|
Format recent track information into a readable message.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
matrix_user (str): The Matrix user ID
|
||||||
|
lastfm_user (str): The Last.fm username
|
||||||
|
data (dict): Data from Last.fm API
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Formatted message
|
||||||
|
"""
|
||||||
|
if not data or "error" in data:
|
||||||
|
return f"🔍 No recent track data found for {lastfm_user}."
|
||||||
|
|
||||||
|
# Check if there's a currently playing track
|
||||||
|
tracks = data.get("recenttracks", {}).get("track", [])
|
||||||
|
if not tracks:
|
||||||
|
return f"🔍 No recent tracks found for {lastfm_user}."
|
||||||
|
|
||||||
|
# Get the most recent track (first in the list)
|
||||||
|
track = tracks[0] if tracks else {}
|
||||||
|
|
||||||
|
# Check if it's currently playing
|
||||||
|
now_playing = track.get("@attr", {}).get("nowplaying", "false") == "true"
|
||||||
|
|
||||||
|
# Extract track information
|
||||||
|
artist = track.get("artist", {}).get("#text", "Unknown Artist")
|
||||||
|
name = track.get("name", "Unknown Track")
|
||||||
|
album = track.get("album", {}).get("#text", "")
|
||||||
|
|
||||||
|
# Create one-line message with bold track name
|
||||||
|
if now_playing:
|
||||||
|
if album:
|
||||||
|
message = f"🎵 {matrix_user} is currently playing: **{name}** by {artist} from the album {album}"
|
||||||
|
else:
|
||||||
|
message = f"🎵 {matrix_user} is currently playing: **{name}** by {artist}"
|
||||||
|
elif tracks:
|
||||||
|
if album:
|
||||||
|
message = f"🎵 {matrix_user} last played: **{name}** by {artist} from the album {album}"
|
||||||
|
else:
|
||||||
|
message = f"🎵 {matrix_user} last played: **{name}** by {artist}"
|
||||||
|
else:
|
||||||
|
message = f"🔍 No recent tracks found for {lastfm_user}."
|
||||||
|
|
||||||
|
# Try to get YouTube link
|
||||||
|
youtube_link = await get_youtube_link(artist, name)
|
||||||
|
if youtube_link:
|
||||||
|
message += f" | [YouTube Link]({youtube_link})"
|
||||||
|
|
||||||
|
return message
|
||||||
|
|
||||||
|
async def handle_command(room, message, bot, prefix, config):
|
||||||
|
"""
|
||||||
|
Function to handle the !register and !np commands.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
room (Room): The Matrix room where the command was invoked.
|
||||||
|
message (RoomMessage): The message object containing the command.
|
||||||
|
bot (Bot): The bot object.
|
||||||
|
prefix (str): The command prefix.
|
||||||
|
config (dict): Configuration parameters.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None
|
||||||
|
"""
|
||||||
|
match = botlib.MessageMatch(room, message, bot, prefix)
|
||||||
|
|
||||||
|
# Initialize database on first run
|
||||||
|
await init_db()
|
||||||
|
|
||||||
|
if match.is_not_from_this_bot() and match.prefix() and match.command("register"):
|
||||||
|
args = match.args()
|
||||||
|
|
||||||
|
if len(args) < 1:
|
||||||
|
await bot.api.send_text_message(
|
||||||
|
room.room_id,
|
||||||
|
"Usage: !register <lastfm_username>"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
lastfm_user = args[0].strip()
|
||||||
|
matrix_user = str(message.sender)
|
||||||
|
|
||||||
|
# Register the Last.fm username
|
||||||
|
await set_lastfm_username(matrix_user, lastfm_user)
|
||||||
|
|
||||||
|
# Confirm registration
|
||||||
|
await bot.api.send_text_message(
|
||||||
|
room.room_id,
|
||||||
|
f"✅ Registered Last.fm user {lastfm_user} for {matrix_user}"
|
||||||
|
)
|
||||||
|
|
||||||
|
logging.info(f"Registered Last.fm user {lastfm_user} for {matrix_user}")
|
||||||
|
|
||||||
|
elif match.is_not_from_this_bot() and match.prefix() and match.command("np"):
|
||||||
|
# Get currently playing track
|
||||||
|
matrix_user = str(message.sender)
|
||||||
|
lastfm_user = await get_lastfm_username(matrix_user)
|
||||||
|
|
||||||
|
if not lastfm_user:
|
||||||
|
await bot.api.send_text_message(
|
||||||
|
room.room_id,
|
||||||
|
f"Please register your Last.fm username first with !register <username>\n"
|
||||||
|
f"Example: !register your_lastfm_username"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Get recent track data
|
||||||
|
track_data = await get_recent_track(lastfm_user)
|
||||||
|
|
||||||
|
# Format and send results
|
||||||
|
result_message = await format_recent_track(matrix_user, lastfm_user, track_data)
|
||||||
|
await bot.api.send_markdown_message(room.room_id, result_message)
|
||||||
|
logging.info(f"Sent Last.fm now playing information for {lastfm_user}")
|
||||||
Reference in New Issue
Block a user