Skip to content

Commit fb611bc

Browse files
committed
feat: show crown on ln commands, revamp graph engine
1 parent 77bc8fc commit fb611bc

File tree

16 files changed

+284
-337
lines changed

16 files changed

+284
-337
lines changed

.idea/sqldialects.xml

-10
This file was deleted.

assets/locales/en/commands.json

+5-7
Original file line numberDiff line numberDiff line change
@@ -37,20 +37,20 @@
3737
"$t(commands:common.lnTitle, {\"name\": \"{{user}}\", \"context\": \"{{isListening}}\"})",
3838
"{{emoji}} **{{track}}**",
3939
"💽 {{album}}",
40-
"🧑‍🎤 {{artist}}",
40+
"{{artistCrown}} {{artist}}",
4141
"$t(commands:common.scrobbles, {\"count\": {{playCount}}})",
4242
"{{-tags}}"
4343
],
4444
"album": [
4545
"$t(commands:common.lnTitle, {\"name\": \"{{user}}\", \"context\": \"{{isListening}}\"})",
4646
"💽 **{{album}}**",
47-
"🧑‍🎤 {{artist}}",
47+
"{{artistCrown}} {{artist}}",
4848
"$t(commands:common.scrobbles, {\"count\": {{playCount}}})",
4949
"{{-tags}}"
5050
],
5151
"artist": [
5252
"$t(commands:common.lnTitle, {\"name\": \"{{user}}\", \"context\": \"{{isListening}}\"})",
53-
"🧑‍🎤 **{{artist}}**",
53+
"{{artistCrown}} **{{artist}}**",
5454
"$t(commands:common.scrobbles, {\"count\": {{playCount}}})",
5555
"{{-tags}}"
5656
],
@@ -154,16 +154,14 @@
154154
"noScrobbles": "🤔 You didn't give me an artist name nor are you listening to anything right now. What do you want me to check?"
155155
},
156156
"meartist": [
157-
"$t(commands:common.lnTitle, {\"name\": \"{{user}}\", \"context\": \"{{isListening}}\"})",
157+
"$t(commands:common.lnOther, {\"name\": \"{{user}}\", \"count\": {{playCount}}})",
158158
"🧑‍🎤 **{{artist}}**",
159-
"$t(commands:common.scrobbles, {\"count\": {{playCount}}})",
160159
"{{-tags}}"
161160
],
162161
"mealbum": [
163-
"$t(commands:common.lnTitle, {\"name\": \"{{user}}\", \"context\": \"{{isListening}}\"})",
162+
"$t(commands:common.lnOther, {\"name\": \"{{user}}\", \"count\": {{playCount}}})",
164163
"💽 **{{album}}**",
165164
"🧑‍🎤 {{artist}}",
166-
"$t(commands:common.scrobbles, {\"count\": {{playCount}}})",
167165
"{{-tags}}"
168166
],
169167
"unlinkfm": {

src/commandEngine/commands/noDMs+registered/crowns.ts

+4-8
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
import { Context } from '../../../multiplatformEngine/common/context.js'
2-
import { graphEngine } from '../../../graphEngine/index.js'
3-
4-
type Args = {
5-
artistName: string
6-
}
7-
type InternalArtistType = { name: string, mbid: string | undefined, imageURL: string | undefined, playCount: number }
2+
import { getUserCrowns } from '../../../graphEngine/operations/crowns.js'
3+
import { getArtistDataByMbid } from '../../../graphEngine/operations.js'
84

95
export default async (ctx: Context) => {
106
const username = ctx.targetedUserData?.fmUsername ?? ctx.registeredUserData!.fmUsername
117
const displayName = ctx.targetedUser?.name ?? ctx.registeredUser!.name
128

13-
const crowns = await graphEngine.getUserCrowns(ctx.channel.id, username).then((r) => {
9+
const crowns = await getUserCrowns(ctx.channel.id, username).then((r) => {
1410
if (!r?.length) return undefined
1511
return r.sort((a: any, b: any) => b.createdat - a.createdat)
1612
})
@@ -21,7 +17,7 @@ export default async (ctx: Context) => {
2117
}
2218

2319
// sort by createdAt
24-
const artistNames = await Promise.all(crowns.map((c: any) => graphEngine.getArtistNameByMbid(c.artistmbid)))
20+
const artistNames = await Promise.all(crowns.map((c: any) => getArtistDataByMbid(c.artistmbid).then(a => a.name)))
2521
let crownsText = ''
2622
for (let i = 0; i < crownCount; i++) {
2723
const crown = crowns[i]

src/commandEngine/commands/noDMs+registered/whoknows.ts src/commandEngine/commands/noDMs+registered/whoknowsartist.ts

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { Context } from '../../../multiplatformEngine/common/context.js'
2-
import { fixLanguageFormat } from '../../helpers.js'
32
import { client } from '../../../fmEngine/index.js'
4-
import { getUser, getUserDisplayName, upsertUserDisplayName } from '../../../databaseEngine/index.js'
5-
import { graphEngine } from '../../../graphEngine/index.js'
3+
import { getUserDisplayName, upsertUserDisplayName } from '../../../databaseEngine/index.js'
64
import { hashName } from '../../../utils.js'
5+
import { addUserToGroupList, linkArtistNameToMbid } from '../../../graphEngine/operations.js'
6+
import { getCountPastCrownHolders, tryToStealCrown } from '../../../graphEngine/operations/crowns.js'
77

88
type Args = {
99
artistName: string
@@ -12,7 +12,7 @@ type InternalArtistType = { name: string, mbid: string | undefined, imageURL: st
1212

1313
export default async (ctx: Context, { artistName }: Args) => {
1414
await upsertUserDisplayName(ctx.author.name, ctx.registeredUserData.fmUsername)
15-
await graphEngine.addMemberToGroupList(ctx.channel.id, ctx.registeredUserData.fmUsername)
15+
await addUserToGroupList(ctx.channel.id, ctx.registeredUserData.fmUsername)
1616
let artist: InternalArtistType | undefined = undefined
1717
if (!artistName) {
1818
// get now playing and retrieve artist from there
@@ -31,11 +31,11 @@ export default async (ctx: Context, { artistName }: Args) => {
3131
mbid: artist?.mbid || artObject.mbid,
3232
}
3333
if (!internalArt.mbid) internalArt.mbid = hashName(internalArt.name)
34-
await graphEngine.linkArtistNameToMbid(internalArt.name, internalArt.mbid)
34+
await linkArtistNameToMbid(internalArt.name, internalArt.mbid, artObject.imageURL)
3535

3636
// try to take the crown
37-
const attempt = await graphEngine.tryToStealCrown(ctx.channel.id, internalArt!.mbid!, ctx.registeredUserData.fmUsername.toLowerCase(), artistName, internalArt.playCount)
38-
const holders = await graphEngine.getPastCrownHoldersCount(ctx.channel.id, internalArt.mbid)
37+
const attempt = await tryToStealCrown(ctx.channel.id, internalArt!.mbid!, ctx.registeredUserData.fmUsername.toLowerCase(), artistName, internalArt.playCount)
38+
const holders = await getCountPastCrownHolders(ctx.channel.id, internalArt.mbid)
3939
const pastHolders = holders.map((r) => ctx.t('commands:whoknows.pastHolder', { name: r.name, playCount: r.playCount }))
4040
const currentHolder = attempt.crown ? ctx.t('commands:whoknows.currentHolder', {
4141
name: await getUserDisplayName(attempt.crown.fmusername).then((r: any) => r?.displayName ?? attempt.crown!.fmusername),
@@ -68,7 +68,7 @@ export default async (ctx: Context, { artistName }: Args) => {
6868
}
6969

7070
export const info = {
71-
aliases: ['wk', 'coroa', 'crown'],
71+
aliases: ['wka', 'coroa', 'crown'],
7272
args: [{
7373
name: 'artistName',
7474
required: false,

src/commandEngine/commands/targetable/album.ts

+4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { Context } from '../../../multiplatformEngine/common/context.js'
22
import { getNowPlaying } from '../../../fmEngine/completeNowPlaying.js'
3+
import { checkIfUserHasCrown } from '../../../graphEngine/operations/crowns.js'
34

45
export default async (ctx: Context) => {
56
const data = await getNowPlaying(ctx, 'album')
67
const user = ctx.targetedUser ?? ctx.registeredUser
78
const userData = ctx.targetedUserData ?? ctx.registeredUserData
89

10+
const hasCrown = await checkIfUserHasCrown(ctx.channel.id, userData.fmUsername, data.artistMbid)
11+
912
ctx.reply(`commands:album`, {
1013
user: user.name,
14+
artistCrown: hasCrown ? '👑' : '🧑‍🎤',
1115
isListening: data.isNowPlaying ? 'isPlaying' : 'wasPlaying',
1216
artist: data.artist,
1317
album: data.album,

src/commandEngine/commands/targetable/artist.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
import { Context } from '../../../multiplatformEngine/common/context.js'
22
import { getNowPlaying } from '../../../fmEngine/completeNowPlaying.js'
3-
import { graphEngine } from '../../../graphEngine/index.js'
43
import { warn } from '../../../loggingEngine/logging.js'
4+
import { checkIfUserHasCrown } from '../../../graphEngine/operations/crowns.js'
55

66
export default async (ctx: Context) => {
77
const data = await getNowPlaying(ctx, 'artist')
88
const user = ctx.targetedUser ?? ctx.registeredUser
99
const userData = ctx.targetedUserData ?? ctx.registeredUserData
1010

11+
const hasCrown = await checkIfUserHasCrown(ctx.channel.id, userData.fmUsername, data.artistMbid)
12+
1113
if (!data.mbid) warn('commands.artist', `no mbid found for ${data.artist}`)
12-
if (data.playCount && data.playCount > 1 && data.mbid) await graphEngine.upsertScrobbles(ctx.registeredUserData.fmUsername, data.mbid, data.playCount)
14+
1315
ctx.reply(`commands:artist`, {
1416
user: user.name,
17+
artistCrown: hasCrown ? '👑' : '🧑‍🎤',
1518
isListening: data.isNowPlaying ? 'isPlaying' : 'wasPlaying',
1619
artist: data.artist,
1720
playCount: data.playCount,

src/commandEngine/commands/targetable/listening.ts

+4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { Context } from '../../../multiplatformEngine/common/context.js'
22
import { getNowPlaying } from '../../../fmEngine/completeNowPlaying.js'
3+
import { checkIfUserHasCrown } from '../../../graphEngine/operations/crowns.js'
34

45
export default async (ctx: Context) => {
56
const data = await getNowPlaying(ctx, 'track')
67
const user = ctx.targetedUser ?? ctx.registeredUser
78
const userData = ctx.targetedUserData ?? ctx.registeredUserData
89

10+
const hasCrown = await checkIfUserHasCrown(ctx.channel.id, userData.fmUsername, data.artistMbid)
11+
912
ctx.reply(`commands:listening`, {
1013
user: user.name,
14+
artistCrown: hasCrown ? '👑' : '🧑‍🎤',
1115
isListening: data.isNowPlaying ? 'isPlaying' : 'wasPlaying',
1216
track: data.name,
1317
artist: data.artist,

src/commandEngine/commands/targeted+registered/mealbum.ts

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ export default async (ctx: Context) => {
66

77
ctx.reply(`commands:mealbum`, {
88
user: ctx.registeredUser?.name,
9-
isListening: data.isNowPlaying ? 'isPlaying' : 'wasPlaying',
109
artist: data.artist,
1110
album: data.album,
1211
playCount: data.playCount,

src/commandEngine/commands/targeted+registered/meartist.ts

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ export default async (ctx: Context) => {
66

77
ctx.reply(`commands:meartist`, {
88
user: ctx.registeredUser?.name,
9-
isListening: data.isNowPlaying ? 'isPlaying' : 'wasPlaying',
109
artist: data.artist,
1110
playCount: data.playCount,
1211
tags: ctx.registeredUserData.sendTags ? `\n*${data.tags.map(a => `#${a}`).join(' ')}*` : '',

src/commandEngine/commands/targeted+registered/metrack.ts

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ export default async (ctx: Context) => {
66

77
ctx.reply(`commands:youtrack`, {
88
user: ctx.registeredUser?.name,
9-
isListening: data.isNowPlaying ? 'isPlaying' : 'wasPlaying',
109
track: data.name,
1110
artist: data.artist,
1211
album: data.album,

src/fmEngine/completeNowPlaying.ts

+15-4
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ import { LastfmRecentTracksTrack } from '@musicorum/lastfm/dist/types/packages/u
55
import { LastfmTag } from '@musicorum/lastfm/dist/types/packages/common.js'
66
import { debug } from '../loggingEngine/logging.js'
77
import { hashName } from '../utils.js'
8-
import { graphEngine } from '../graphEngine/index.js'
8+
import { upsertArtistScrobbles } from '../graphEngine/operations/artist-scrobbles.js'
99

1010
export type NowPlayingEntity = 'artist' | 'album' | 'track'
1111

1212
export interface NowPlayingData<NowPlayingEntity> {
1313
name: string
14-
mbid?: string
14+
mbid: string
15+
artistMbid: string
1516
imageURL: string
1617
artist?: string
1718
album?: string
@@ -26,7 +27,9 @@ const entityCall = async (entity: NowPlayingEntity, username: string, track: Las
2627

2728
switch (entity) {
2829
case 'artist':
29-
return client.artist.getInfo(track.artist.name, data)
30+
const d = await client.artist.getInfo(track.artist.name, data)
31+
if (d?.playCount && d?.playCount > 1 && d?.mbid) await upsertArtistScrobbles(username, d?.mbid, d?.playCount)
32+
return d
3033
case 'album':
3134
return client.album.getInfo(track.album.name, track.artist.name, data)
3235
case 'track':
@@ -54,12 +57,20 @@ export const getNowPlaying = async (ctx: Context, entity: NowPlayingEntity, getF
5457
track
5558
).catch(() => undefined)
5659

57-
let tags = info.tags?.map?.((tag: LastfmTag) => tag.name ?? tag) || []
60+
let tags = info?.tags?.map?.((tag: LastfmTag) => tag.name ?? tag) || []
5861
tags = tags.map((a: string) => a.toLowerCase().replaceAll('-', '_').replaceAll(' ', '_'))
5962

63+
let artistMbid
64+
if (entity === 'track' || entity === 'album') {
65+
artistMbid = track.artist.mbid || hashName(track.artist.name)
66+
} else {
67+
artistMbid = info?.mbid
68+
}
69+
6070
return {
6171
name: track.name,
6272
mbid: info.mbid || hashName(track.name),
73+
artistMbid,
6374
imageURL: info.images?.[3]?.url || track?.images?.[3]?.url,
6475
artist: track.artist.name,
6576
album: track.album.name || info.album?.name,

src/graphEngine/index.ts

+1-80
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,17 @@
11
import { createKeyspace, createTables } from './migrations.js'
22
import { Client } from 'cassandra-driver'
33
import { debug, error } from '../loggingEngine/logging.js'
4-
import {
5-
getCrown,
6-
upsertArtistScrobble,
7-
getUserCrowns,
8-
tryGetToCrown,
9-
addUserToGroupList,
10-
getCountPastCrownHolders, linkArtistNameToMbid, getArtistNameFromMbid
11-
} from './operations.js'
124

13-
const client = new Client({
5+
export const client = new Client({
146
contactPoints: [process.env.CASSANDRA_HOST || 'localhost'],
157
localDataCenter: 'datacenter1'
168
})
179

18-
class GraphEngine {
19-
hasStarted: boolean = false
20-
client: Client | undefined = undefined
21-
22-
upsertScrobbles (fmUsername: string, artistMbid: string, playCount: number) {
23-
if (!this.hasStarted) {
24-
throw new Error('GraphEngine has not started')
25-
}
26-
debug('graphEngine.upsertScrobbles', `upserting scrobbles for ${fmUsername} on ${artistMbid} with playcount ${playCount}`)
27-
return upsertArtistScrobble(this.client!, fmUsername, artistMbid, playCount)
28-
}
29-
30-
getCrownOnGroup (groupId: string, artistMbid: string) {
31-
if (!this.hasStarted) {
32-
throw new Error('GraphEngine has not started')
33-
}
34-
return getCrown(this.client!, groupId, artistMbid)
35-
}
36-
37-
getUserCrowns (groupId: string, fmUsername: string) {
38-
if (!this.hasStarted) {
39-
throw new Error('GraphEngine has not started')
40-
}
41-
return getUserCrowns(this.client!, groupId, fmUsername)
42-
}
43-
44-
tryToStealCrown (groupId: string, artistMbid: string, fmUsername: string, artistName: string, playCount: number = 0) {
45-
if (!this.hasStarted) {
46-
throw new Error('GraphEngine has not started')
47-
}
48-
return tryGetToCrown(this.client!, groupId, artistMbid, fmUsername, artistName, playCount)
49-
}
50-
51-
addMemberToGroupList (groupId: string, fmUsername: string) {
52-
if (!this.hasStarted) {
53-
throw new Error('GraphEngine has not started')
54-
}
55-
return addUserToGroupList(this.client!, groupId, fmUsername).then(() => debug('graphEngine.addMemberToGroupList', `added ${fmUsername} to group ${groupId}`))
56-
}
57-
58-
setClient (client: Client) {
59-
this.client = client
60-
this.hasStarted = true
61-
}
62-
63-
getPastCrownHoldersCount (groupId: string, artistMbid: string) {
64-
if (!this.hasStarted) {
65-
throw new Error('GraphEngine has not started')
66-
}
67-
return getCountPastCrownHolders(this.client!, groupId, artistMbid)
68-
}
69-
70-
linkArtistNameToMbid (artistName: string, artistMbid: string | undefined) {
71-
if (!this.hasStarted) {
72-
throw new Error('GraphEngine has not started')
73-
}
74-
return linkArtistNameToMbid(this.client!, artistName, artistMbid)
75-
}
76-
77-
getArtistNameByMbid (artistMbid: string) {
78-
if (!this.hasStarted) {
79-
throw new Error('GraphEngine has not started')
80-
}
81-
82-
return getArtistNameFromMbid(this.client!, artistMbid)
83-
}
84-
}
85-
86-
export const graphEngine = new GraphEngine()
87-
8810
export const start = async () => {
8911
await client.connect().then(() => debug('graphEngine.main', 'connected to database'))
9012
await createKeyspace(client).then(() => debug('graphEngine.main', 'keyspace created')).catch((e) => error('graphEngine.main', e.stack))
9113
client.keyspace = 'lastgram'
9214

9315
await createTables(client).then(() => debug('graphEngine.main', 'tables created')).catch((e) => error('graphEngine.main', e.stack))
94-
graphEngine.setClient(client)
9516
}
9617

src/graphEngine/migrations.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export const createTables = async (client: Client) => {
4040
fmUsername text,
4141
playCount int,
4242
createdAt timestamp,
43-
PRIMARY KEY (groupId, artistMbid)
43+
PRIMARY KEY (groupId, artistMbid, fmUsername)
4444
);
4545
`
4646

@@ -49,6 +49,7 @@ export const createTables = async (client: Client) => {
4949
CREATE TABLE IF NOT EXISTS artist_mbid_map (
5050
artistName text,
5151
artistMbid text,
52+
artistCover text,
5253
PRIMARY KEY (artistName, artistMbid)
5354
);
5455
`

0 commit comments

Comments
 (0)