Add linter and minor bug fixes (#107)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, data, rest, q, account, imports}, {enabled = false} = {}) {
|
||||
export default async function({login, data, rest, q, account, imports}, {enabled = false} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
|
||||
@@ -34,6 +34,7 @@ inputs:
|
||||
type: array
|
||||
format: comma-separated
|
||||
default: all
|
||||
example: issue, pr, review, wiki, star
|
||||
values:
|
||||
- all # Display all types of events
|
||||
- comment # Display commits, issues and pull requests comments
|
||||
@@ -48,4 +49,4 @@ inputs:
|
||||
- fork # Display forked repositories
|
||||
- star # Display starred repositories
|
||||
- member # Display collaborators additions
|
||||
- public # Display repositories made public
|
||||
- public # Display repositories made public
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, data, queries, imports, q, account}, {enabled = false} = {}) {
|
||||
export default async function({login, data, queries, imports, q, account}, {enabled = false} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -31,7 +31,7 @@
|
||||
//Format and save results
|
||||
for (const {name, entries} of lists) {
|
||||
//Format results
|
||||
const list = await Promise.all(entries.map(async media => await format({media, imports})))
|
||||
const list = await Promise.all(entries.map(media => format({media, imports})))
|
||||
result.lists[type][name.toLocaleLowerCase()] = shuffle ? imports.shuffle(list) : list
|
||||
//Limit results
|
||||
if (limit > 0) {
|
||||
@@ -131,7 +131,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
/** Media formatter */
|
||||
/**Media formatter */
|
||||
async function format({media, imports}) {
|
||||
const {progress, score:userScore, media:{title, description, status, startDate:{year:release}, genres, averageScore, episodes, chapters, type, coverImage:{medium:artwork}}} = media
|
||||
return {
|
||||
@@ -140,6 +140,6 @@
|
||||
description:description.replace(/<br\s*\\?>/g, " "),
|
||||
scores:{user:userScore, community:averageScore},
|
||||
released:type === "ANIME" ? episodes : chapters,
|
||||
artwork:artwork ? await imports.imgb64(artwork) : "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOcOnfpfwAGfgLYttYINwAAAABJRU5ErkJggg=="
|
||||
artwork:artwork ? await imports.imgb64(artwork) : "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mOcOnfpfwAGfgLYttYINwAAAABJRU5ErkJggg==",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ inputs:
|
||||
type: array
|
||||
format: comma-separated
|
||||
default: favorites
|
||||
example: favorites, watching, characters
|
||||
values:
|
||||
- favorites # Favorites animes and mangas (depending on plugin_anilist_medias values)
|
||||
- watching # Animes in your watching list
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
//Setup
|
||||
export default async function ({login, graphql, data, q, queries, imports}, conf) {
|
||||
export default async function({login, graphql, data, q, queries, imports}, conf) {
|
||||
//Load inputs
|
||||
console.debug(`metrics/compute/${login}/base > started`)
|
||||
let {repositories, repositories_forks:forks} = imports.metadata.plugins.base.inputs({data, q, account:"bypass"}, {repositories:conf.settings.repositories ?? 100})
|
||||
@@ -16,7 +16,7 @@
|
||||
//Base parts (legacy handling for web instance)
|
||||
const defaulted = ("base" in q) ? legacy.converter(q.base) ?? true : true
|
||||
for (const part of conf.settings.plugins.base.parts)
|
||||
data.base[part] = `base.${part}` in q ? legacy.converter(q[ `base.${part}`]) : defaulted
|
||||
data.base[part] = `base.${part}` in q ? legacy.converter(q[`base.${part}`]) : defaulted
|
||||
|
||||
//Iterate through account types
|
||||
for (const account of ["user", "organization"]) {
|
||||
@@ -46,7 +46,8 @@
|
||||
//Success
|
||||
console.debug(`metrics/compute/${login}/base > graphql query > account ${account} > success`)
|
||||
return {}
|
||||
} catch (error) {
|
||||
}
|
||||
catch (error) {
|
||||
console.debug(`metrics/compute/${login}/base > account ${account} > failed : ${error}`)
|
||||
console.debug(`metrics/compute/${login}/base > checking next account`)
|
||||
}
|
||||
@@ -69,7 +70,7 @@
|
||||
//Organization
|
||||
organization({login, data}) {
|
||||
console.debug(`metrics/compute/${login}/base > applying postprocessing`)
|
||||
data.account = "organization",
|
||||
data.account = "organization"
|
||||
Object.assign(data.user, {
|
||||
isHireable:false,
|
||||
starredRepositories:{totalCount:0},
|
||||
@@ -107,7 +108,7 @@
|
||||
repositories:{totalCount:0, totalDiskUsage:0, nodes:[]},
|
||||
packages:{totalCount:0},
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
//Legacy functions
|
||||
@@ -119,5 +120,5 @@
|
||||
return false
|
||||
if (Number.isFinite(Number(value)))
|
||||
return !!(Number(value))
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
@@ -4,22 +4,25 @@
|
||||
*/
|
||||
|
||||
//Setup
|
||||
export default async function ({login, q, dflags}, {conf, data, rest, graphql, plugins, queries, account}, {pending, imports}) {
|
||||
export default async function({login, q, dflags}, {conf, data, rest, graphql, plugins, queries, account}, {pending, imports}) {
|
||||
//Load inputs
|
||||
imports.metadata.plugins.core.inputs({data, account, q})
|
||||
|
||||
//Init
|
||||
const computed = data.computed = {commits:0, sponsorships:0, licenses:{favorite:"", used:{}}, token:{}, repositories:{watchers:0, stargazers:0, issues_open:0, issues_closed:0, pr_open:0, pr_merged:0, forks:0, forked:0, releases:0}}
|
||||
const computed = {commits:0, sponsorships:0, licenses:{favorite:"", used:{}}, token:{}, repositories:{watchers:0, stargazers:0, issues_open:0, issues_closed:0, pr_open:0, pr_merged:0, forks:0, forked:0, releases:0}}
|
||||
const avatar = imports.imgb64(data.user.avatarUrl)
|
||||
data.computed = computed
|
||||
console.debug(`metrics/compute/${login} > formatting common metrics`)
|
||||
|
||||
//Timezone config
|
||||
if (q["config.timezone"]) {
|
||||
const timezone = data.config.timezone = {name:q["config.timezone"], offset:0}
|
||||
const timezone = {name:q["config.timezone"], offset:0}
|
||||
data.config.timezone = timezone
|
||||
try {
|
||||
timezone.offset = Number(new Date().toLocaleString("fr", {timeZoneName:"short", timeZone:timezone.name}).match(/UTC[+](?<offset>\d+)/)?.groups?.offset*60*60*1000) || 0
|
||||
console.debug(`metrics/compute/${login} > timezone set to ${timezone.name} (${timezone.offset > 0 ? "+" : ""}${Math.round(timezone.offset/(60*60*1000))} hours)`)
|
||||
} catch {
|
||||
}
|
||||
catch {
|
||||
timezone.error = `Failed to use timezone "${timezone.name}"`
|
||||
console.debug(`metrics/compute/${login} > failed to use timezone "${timezone.name}"`)
|
||||
}
|
||||
@@ -35,7 +38,7 @@
|
||||
for (const name of Object.keys(imports.plugins)) {
|
||||
if (!plugins[name]?.enabled)
|
||||
continue
|
||||
pending.push((async () => {
|
||||
pending.push((async() => {
|
||||
try {
|
||||
console.debug(`metrics/compute/${login}/plugins > ${name} > started`)
|
||||
data.plugins[name] = await imports.plugins[name]({login, q, imports, data, computed, rest, graphql, queries, account}, plugins[name])
|
||||
@@ -71,7 +74,7 @@
|
||||
computed.diskUsage = `${imports.bytes(data.user.repositories.totalDiskUsage*1000)}`
|
||||
|
||||
//Compute licenses stats
|
||||
computed.licenses.favorite = Object.entries(computed.licenses.used).sort(([an, a], [bn, b]) => b - a).slice(0, 1).map(([name, value]) => name) ?? ""
|
||||
computed.licenses.favorite = Object.entries(computed.licenses.used).sort(([_an, a], [_bn, b]) => b - a).slice(0, 1).map(([name, _value]) => name) ?? ""
|
||||
|
||||
//Compute total commits
|
||||
computed.commits += data.user.contributionsCollection.totalCommitContributions + data.user.contributionsCollection.restrictedContributionsCount
|
||||
@@ -117,7 +120,7 @@
|
||||
computed.calendar.map(day => day.color = halloween(day.color))
|
||||
//Update isocalendar colors
|
||||
const waiting = [...pending]
|
||||
pending.push((async () => {
|
||||
pending.push((async() => {
|
||||
await Promise.all(waiting)
|
||||
if (data.plugins.isocalendar?.svg)
|
||||
data.plugins.isocalendar.svg = halloween(data.plugins.isocalendar.svg)
|
||||
@@ -127,4 +130,4 @@
|
||||
|
||||
//Results
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({data, computed, imports, q, account}, {enabled = false} = {}) {
|
||||
export default async function({data, computed, imports, q, account}, {enabled = false} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -12,15 +12,27 @@
|
||||
//Define getters
|
||||
const followup = {
|
||||
issues:{
|
||||
get count() { return this.open + this.closed },
|
||||
get open() { return computed.repositories.issues_open },
|
||||
get closed() { return computed.repositories.issues_closed },
|
||||
get count() {
|
||||
return this.open + this.closed
|
||||
},
|
||||
get open() {
|
||||
return computed.repositories.issues_open
|
||||
},
|
||||
get closed() {
|
||||
return computed.repositories.issues_closed
|
||||
},
|
||||
},
|
||||
pr:{
|
||||
get count() { return this.open + this.merged },
|
||||
get open() { return computed.repositories.pr_open },
|
||||
get merged() { return computed.repositories.pr_merged }
|
||||
}
|
||||
get count() {
|
||||
return this.open + this.merged
|
||||
},
|
||||
get open() {
|
||||
return computed.repositories.pr_open
|
||||
},
|
||||
get merged() {
|
||||
return computed.repositories.pr_merged
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
//Results
|
||||
@@ -30,4 +42,4 @@
|
||||
catch (error) {
|
||||
throw {error:{message:"An error occured", instance:error}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, data, graphql, q, imports, queries, account}, {enabled = false} = {}) {
|
||||
export default async function({login, data, graphql, q, imports, queries, account}, {enabled = false} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
//Iterate through gists
|
||||
console.debug(`metrics/compute/${login}/plugins > gists > processing ${gists.length} gists`)
|
||||
let stargazers = 0, forks = 0, comments = 0, files = 0
|
||||
let comments = 0, files = 0, forks = 0, stargazers = 0
|
||||
for (const gist of gists) {
|
||||
//Skip forks
|
||||
if (gist.isFork)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, data, rest, imports, q, account}, {enabled = false, ...defaults} = {}) {
|
||||
export default async function({login, data, rest, imports, q, account}, {enabled = false, ...defaults} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -22,7 +22,10 @@
|
||||
console.debug(`metrics/compute/${login}/plugins > habits > loading page ${page}`)
|
||||
events.push(...(await rest.activity.listEventsForAuthenticatedUser({username:login, per_page:100, page})).data)
|
||||
}
|
||||
} catch { console.debug(`metrics/compute/${login}/plugins > habits > no more page to load`) }
|
||||
}
|
||||
catch {
|
||||
console.debug(`metrics/compute/${login}/plugins > habits > no more page to load`)
|
||||
}
|
||||
console.debug(`metrics/compute/${login}/plugins > habits > ${events.length} events loaded`)
|
||||
|
||||
//Get user recent commits
|
||||
@@ -36,8 +39,7 @@
|
||||
console.debug(`metrics/compute/${login}/plugins > habits > loading patches`)
|
||||
const patches = [...await Promise.allSettled(commits
|
||||
.flatMap(({payload}) => payload.commits).map(commit => commit.url)
|
||||
.map(async commit => (await rest.request(commit)).data.files)
|
||||
)]
|
||||
.map(async commit => (await rest.request(commit)).data.files))]
|
||||
.filter(({status}) => status === "fulfilled")
|
||||
.map(({value}) => value)
|
||||
.flatMap(files => files.map(file => ({name:imports.paths.basename(file.filename), patch:file.patch ?? ""})))
|
||||
@@ -52,7 +54,7 @@
|
||||
habits.commits.days[day] = (habits.commits.days[day] ?? 0) + 1
|
||||
habits.commits.days.max = Math.max(...Object.values(habits.commits.days))
|
||||
//Compute day with most commits
|
||||
habits.commits.day = days.length ? ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][Object.entries(habits.commits.days).sort(([an, a], [bn, b]) => b - a).map(([day, occurence]) => day)[0]] ?? NaN : NaN
|
||||
habits.commits.day = days.length ? ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"][Object.entries(habits.commits.days).sort(([_an, a], [_bn, b]) => b - a).map(([day, _occurence]) => day)[0]] ?? NaN : NaN
|
||||
}
|
||||
|
||||
//Commit hour
|
||||
@@ -64,7 +66,7 @@
|
||||
habits.commits.hours[hour] = (habits.commits.hours[hour] ?? 0) + 1
|
||||
habits.commits.hours.max = Math.max(...Object.values(habits.commits.hours))
|
||||
//Compute hour with most commits
|
||||
habits.commits.hour = hours.length ? `${Object.entries(habits.commits.hours).sort(([an, a], [bn, b]) => b - a).map(([hour, occurence]) => hour)[0]}`.padStart(2, "0") : NaN
|
||||
habits.commits.hour = hours.length ? `${Object.entries(habits.commits.hours).sort(([_an, a], [_bn, b]) => b - a).map(([hour, _occurence]) => hour)[0]}`.padStart(2, "0") : NaN
|
||||
}
|
||||
|
||||
//Indent style
|
||||
@@ -72,7 +74,7 @@
|
||||
//Attempt to guess whether tabs or spaces are used in patches
|
||||
console.debug(`metrics/compute/${login}/plugins > habits > searching indent style`)
|
||||
patches
|
||||
.map(({patch}) => patch.match(/((?:\t)|(?: )) /gm) ?? [])
|
||||
.map(({patch}) => patch.match(/((?:\t)|(?:[ ]{2})) /gm) ?? []) //eslint-disable-line prefer-named-capture-group
|
||||
.forEach(indent => habits.indents[/^\t/.test(indent) ? "tabs" : "spaces"]++)
|
||||
habits.indents.style = habits.indents.spaces > habits.indents.tabs ? "spaces" : habits.indents.tabs > habits.indents.spaces ? "tabs" : ""
|
||||
}
|
||||
@@ -89,18 +91,18 @@
|
||||
//Create temporary directory and save patches
|
||||
console.debug(`metrics/compute/${login}/plugins > habits > creating temp dir ${path} with ${patches.length} files`)
|
||||
await imports.fs.mkdir(path, {recursive:true})
|
||||
await Promise.all(patches.map(async ({name, patch}, i) => await imports.fs.writeFile(imports.paths.join(path, `${i}${imports.paths.extname(name)}`), patch)))
|
||||
await Promise.all(patches.map(({name, patch}, i) => imports.fs.writeFile(imports.paths.join(path, `${i}${imports.paths.extname(name)}`), patch)))
|
||||
//Create temporary git repository
|
||||
console.debug(`metrics/compute/${login}/plugins > habits > creating temp git repository`)
|
||||
await imports.run(`git init && git add . && git config user.name "linguist" && git config user.email "<>" && git commit -m "linguist"`, {cwd:path}).catch(console.debug)
|
||||
await imports.run(`git status`, {cwd:path})
|
||||
await imports.run('git init && git add . && git config user.name "linguist" && git config user.email "<>" && git commit -m "linguist"', {cwd:path}).catch(console.debug)
|
||||
await imports.run("git status", {cwd:path})
|
||||
//Spawn linguist process
|
||||
console.debug(`metrics/compute/${login}/plugins > habits > running linguist`)
|
||||
;(await imports.run(`${prefix} github-linguist --breakdown`, {cwd:path}))
|
||||
//Parse linguist result
|
||||
.split("\n").map(line => line.match(/(?<value>[\d.]+)%\s+(?<language>\w+)/)?.groups).filter(line => line)
|
||||
.map(({value, language}) => habits.linguist.languages[language] = (habits.linguist.languages[language] ?? 0) + value/100)
|
||||
habits.linguist.ordered = Object.entries(habits.linguist.languages).sort(([an, a], [bn, b]) => b - a)
|
||||
habits.linguist.ordered = Object.entries(habits.linguist.languages).sort(([_an, a], [_bn, b]) => b - a)
|
||||
}
|
||||
else
|
||||
console.debug(`metrics/compute/${login}/plugins > habits > linguist not available`)
|
||||
@@ -115,4 +117,4 @@
|
||||
throw error
|
||||
throw {error:{message:"An error occured", instance:error}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, data, graphql, q, imports, queries, account}, {enabled = false} = {}) {
|
||||
export default async function({login, data, graphql, q, imports, queries, account}, {enabled = false} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
//Compute the highest contributions in a day, streaks and average commits per day
|
||||
console.debug(`metrics/compute/${login}/plugins > isocalendar > computing stats`)
|
||||
let max = 0, streak = {max:0, current:0}, values = [], average = 0
|
||||
let average = 0, max = 0, streak = {max:0, current:0}, values = []
|
||||
for (const week of calendar.weeks) {
|
||||
for (const day of week.contributionDays) {
|
||||
values.push(day.contributionCount)
|
||||
@@ -61,8 +61,8 @@
|
||||
<feComponentTransfer>
|
||||
${[..."RGB"].map(channel => `<feFunc${channel} type="linear" slope="${1-k*0.4}" />`).join("")}
|
||||
</feComponentTransfer>
|
||||
</filter>`
|
||||
).join("")}
|
||||
</filter>`)
|
||||
.join("")}
|
||||
<g transform="scale(4) translate(12, 0)">`
|
||||
//Iterate through weeks
|
||||
for (const week of calendar.weeks) {
|
||||
@@ -79,10 +79,10 @@
|
||||
</g>`
|
||||
j++
|
||||
}
|
||||
svg += `</g>`
|
||||
svg += "</g>"
|
||||
i++
|
||||
}
|
||||
svg += `</g></svg>`
|
||||
svg += "</g></svg>"
|
||||
|
||||
//Results
|
||||
return {streak, max, average, svg, duration}
|
||||
@@ -93,4 +93,4 @@
|
||||
throw error
|
||||
throw {error:{message:"An error occured", instance:error}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, data, imports, q, account}, {enabled = false} = {}) {
|
||||
export default async function({login, data, imports, q, account}, {enabled = false} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
//Compute languages stats
|
||||
console.debug(`metrics/compute/${login}/plugins > languages > computing stats`)
|
||||
languages.favorites = Object.entries(languages.stats).sort(([an, a], [bn, b]) => b - a).slice(0, 8).map(([name, value]) => ({name, value, size:value, color:languages.colors[name], x:0})).filter(({value}) => value/languages.total > threshold)
|
||||
languages.favorites = Object.entries(languages.stats).sort(([_an, a], [_bn, b]) => b - a).slice(0, 8).map(([name, value]) => ({name, value, size:value, color:languages.colors[name], x:0})).filter(({value}) => value/languages.total > threshold)
|
||||
const visible = {total:Object.values(languages.favorites).map(({size}) => size).reduce((a, b) => a + b, 0)}
|
||||
for (let i = 0; i < languages.favorites.length; i++) {
|
||||
languages.favorites[i].value /= visible.total
|
||||
|
||||
@@ -18,6 +18,7 @@ inputs:
|
||||
type: array
|
||||
format: comma-separated
|
||||
default: ""
|
||||
example: html, css, ...
|
||||
|
||||
# List of repositories that will be skipped
|
||||
plugin_languages_skipped:
|
||||
@@ -25,6 +26,7 @@ inputs:
|
||||
type: array
|
||||
format: comma-separated
|
||||
default: ""
|
||||
example: my-repo-1, my-repo-2, ...
|
||||
|
||||
# Overrides default languages colors
|
||||
# Use `${n}:${color}` to change the color of the n-th most used language (e.g. "0:red" to make your most used language red)
|
||||
@@ -38,6 +40,7 @@ inputs:
|
||||
- comma-separated
|
||||
- /((?<index>[0-9])|(?<language>[-+a-z0-9#])):(?<color>#?[-a-z0-9]+)/
|
||||
default: github
|
||||
example: javascript:red, 0:blue, 1:#ff00aa
|
||||
|
||||
# Languages additional details
|
||||
plugin_languages_details:
|
||||
@@ -48,6 +51,7 @@ inputs:
|
||||
- bytes-size # Languages total size written in bytes
|
||||
- percentage # Languages proportions in %
|
||||
default: ""
|
||||
example: bytes-size, percentage
|
||||
|
||||
# Minimum threshold (in percentage) to reach for languages to be displayed
|
||||
plugin_languages_threshold:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, data, imports, rest, q, account}, {enabled = false} = {}) {
|
||||
export default async function({login, data, imports, rest, q, account}, {enabled = false} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -22,7 +22,7 @@
|
||||
//Get contributors stats from repositories
|
||||
console.debug(`metrics/compute/${login}/plugins > lines > querying api`)
|
||||
const lines = {added:0, deleted:0}
|
||||
const response = await Promise.all(repositories.map(async ({repo, owner}) => await rest.repos.getContributorsStats({owner, repo})))
|
||||
const response = await Promise.all(repositories.map(({repo, owner}) => rest.repos.getContributorsStats({owner, repo})))
|
||||
|
||||
//Compute changed lines
|
||||
console.debug(`metrics/compute/${login}/plugins > lines > computing total diff`)
|
||||
|
||||
@@ -180,8 +180,8 @@ Register your API key to finish setup.
|
||||
plugin_music_provider: spotify # Use Spotify as provider
|
||||
plugin_music_mode: recent # Set plugin mode
|
||||
plugin_music_limit: 4 # Limit to 4 entries
|
||||
plugin_music_played_at: yes # Show timestamp (only works with spotify, 🚧 @master feature)
|
||||
plugin_music_token: "${{ secrets.SPOTIFY_CLIENT_ID }}, ${{ secrets.SPOTIFY_CLIENT_SECRET }}, ${{ secrets.SPOTIFY_REFRESH_TOKEN }}"
|
||||
plugin_music_played_at: yes # Only works with spotify.
|
||||
```
|
||||
|
||||
```yaml
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
}
|
||||
|
||||
//Setup
|
||||
export default async function ({login, imports, data, q, account}, {enabled = false, token = ""} = {}) {
|
||||
export default async function({login, imports, data, q, account}, {enabled = false, token = ""} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -29,34 +29,40 @@
|
||||
|
||||
//Initialization
|
||||
const raw = {
|
||||
get provider() { return providers[provider]?.name ?? "" },
|
||||
get mode() { return modes[mode] ?? "Unconfigured music plugin"},
|
||||
get provider() {
|
||||
return providers[provider]?.name ?? ""
|
||||
},
|
||||
get mode() {
|
||||
return modes[mode] ?? "Unconfigured music plugin"
|
||||
},
|
||||
}
|
||||
let tracks = null
|
||||
|
||||
//Load inputs
|
||||
let {provider, mode, playlist, limit, user, played_at} = imports.metadata.plugins.music.inputs({data, account, q})
|
||||
let {provider, mode, playlist, limit, user, "played.at":played_at} = imports.metadata.plugins.music.inputs({data, account, q})
|
||||
//Auto-guess parameters
|
||||
if ((playlist)&&(!mode))
|
||||
mode = "playlist"
|
||||
if ((playlist)&&(!provider))
|
||||
for (const [name, {embed}] of Object.entries(providers))
|
||||
if ((playlist)&&(!provider)) {
|
||||
for (const [name, {embed}] of Object.entries(providers)) {
|
||||
if (embed.test(playlist))
|
||||
provider = name
|
||||
}
|
||||
}
|
||||
if (!mode)
|
||||
mode = "recent"
|
||||
//Provider
|
||||
if (!(provider in providers))
|
||||
throw {error:{message:provider ? `Unsupported provider "${provider}"` : `Missing provider`}, ...raw}
|
||||
throw {error:{message:provider ? `Unsupported provider "${provider}"` : "Missing provider"}, ...raw}
|
||||
//Mode
|
||||
if (!(mode in modes))
|
||||
throw {error:{message:`Unsupported mode "${mode}"`}, ...raw}
|
||||
//Playlist mode
|
||||
if (mode === "playlist") {
|
||||
if (!playlist)
|
||||
throw {error:{message:`Missing playlist url`}, ...raw}
|
||||
throw {error:{message:"Missing playlist url"}, ...raw}
|
||||
if (!providers[provider].embed.test(playlist))
|
||||
throw {error:{message:`Unsupported playlist url format`}, ...raw}
|
||||
throw {error:{message:"Unsupported playlist url format"}, ...raw}
|
||||
}
|
||||
//Limit
|
||||
limit = Math.max(1, Math.min(100, Number(limit)))
|
||||
@@ -83,7 +89,7 @@
|
||||
tracks = [...await frame.evaluate(() => [...document.querySelectorAll(".tracklist li")].map(li => ({
|
||||
name:li.querySelector(".tracklist__track__name").innerText,
|
||||
artist:li.querySelector(".tracklist__track__sub").innerText,
|
||||
artwork:li.querySelector(".tracklist__track__artwork img").src
|
||||
artwork:li.querySelector(".tracklist__track__artwork img").src,
|
||||
})))]
|
||||
break
|
||||
}
|
||||
@@ -95,7 +101,7 @@
|
||||
name:tr.querySelector("td:nth-child(2) div:nth-child(1)").innerText,
|
||||
artist:tr.querySelector("td:nth-child(2) div:nth-child(2)").innerText,
|
||||
//Spotify doesn't provide artworks so we fallback on playlist artwork instead
|
||||
artwork:window.getComputedStyle(document.querySelector("button[title=Play]").parentNode, null).backgroundImage.match(/^url\("(https:...+)"\)$/)[1]
|
||||
artwork:window.getComputedStyle(document.querySelector("button[title=Play]").parentNode, null).backgroundImage.match(/^url\("(?<url>https:...+)"\)$/)?.groups?.url ?? null,
|
||||
})))]
|
||||
break
|
||||
}
|
||||
@@ -118,8 +124,6 @@
|
||||
}
|
||||
//Recently played
|
||||
case "recent":{
|
||||
//Initialisation
|
||||
const timestamp = Date.now()-24*60*60*1000
|
||||
//Handle provider
|
||||
switch (provider) {
|
||||
//Spotify
|
||||
@@ -127,28 +131,40 @@
|
||||
//Prepare credentials
|
||||
const [client_id, client_secret, refresh_token] = token.split(",").map(part => part.trim())
|
||||
if ((!client_id)||(!client_secret)||(!refresh_token))
|
||||
throw {error:{message:`Spotify token must contain client id/secret and refresh token`}}
|
||||
throw {error:{message:"Spotify token must contain client id/secret and refresh token"}}
|
||||
//API call and parse tracklist
|
||||
try {
|
||||
//Request access token
|
||||
console.debug(`metrics/compute/${login}/plugins > music > requesting access token with spotify refresh token`)
|
||||
const {data:{access_token:access}} = await imports.axios.post("https://accounts.spotify.com/api/token",
|
||||
`${new imports.url.URLSearchParams({grant_type:"refresh_token", refresh_token, client_id, client_secret})}`,
|
||||
{headers:{"Content-Type":"application/x-www-form-urlencoded"}},
|
||||
)
|
||||
const {data:{access_token:access}} = await imports.axios.post("https://accounts.spotify.com/api/token", `${new imports.url.URLSearchParams({grant_type:"refresh_token", refresh_token, client_id, client_secret})}`, {headers:{
|
||||
"Content-Type":"application/x-www-form-urlencoded",
|
||||
}})
|
||||
console.debug(`metrics/compute/${login}/plugins > music > got access token`)
|
||||
//Retrieve tracks
|
||||
console.debug(`metrics/compute/${login}/plugins > music > querying spotify api`)
|
||||
tracks = (await imports.axios.get(`https://api.spotify.com/v1/me/player/recently-played?limit=${limit}&after=${timestamp}`, {headers:{
|
||||
"Accept":"application/json",
|
||||
"Content-Type":"application/json",
|
||||
"Authorization":`Bearer ${access}`}
|
||||
})).data.items.map(({track, played_at}) => ({
|
||||
name:track.name,
|
||||
artist:track.artists[0].name,
|
||||
artwork:track.album.images[0].url,
|
||||
played_at: played_at ? imports.dayjs(played_at).format('[played at] HH:MM on DD/MM/YYYY') : ''
|
||||
}))
|
||||
tracks = []
|
||||
for (let hours = .5; hours <= 24; hours++) {
|
||||
//Load track half-hour by half-hour
|
||||
const timestamp = Date.now()-hours*60*60*1000
|
||||
const loaded = (await imports.axios.get(`https://api.spotify.com/v1/me/player/recently-played?after=${timestamp}`, {headers:{
|
||||
"Content-Type":"application/json",
|
||||
Accept:"application/json",
|
||||
Authorization:`Bearer ${access}`,
|
||||
}})).data.items.map(({track, played_at}) => ({
|
||||
name:track.name,
|
||||
artist:track.artists[0].name,
|
||||
artwork:track.album.images[0].url,
|
||||
played_at:played_at ? imports.dayjs(played_at).format("[played at] HH:MM on DD/MM/YYYY") : null,
|
||||
}))
|
||||
//Ensure no duplicate are added
|
||||
for (const track of loaded) {
|
||||
if (!tracks.map(({name}) => name).includes(track.name))
|
||||
tracks.push(track)
|
||||
}
|
||||
//Early break
|
||||
if (tracks.length >= limit)
|
||||
break
|
||||
}
|
||||
}
|
||||
//Handle errors
|
||||
catch (error) {
|
||||
@@ -169,9 +185,9 @@
|
||||
try {
|
||||
console.debug(`metrics/compute/${login}/plugins > music > querying lastfm api`)
|
||||
tracks = (await imports.axios.get(`https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=${user}&api_key=${token}&limit=${limit}&format=json`, {headers:{
|
||||
"Accept":"application/json",
|
||||
"User-Agent":"lowlighter/metrics"}
|
||||
})).data.recenttracks.track.map((track) => ({
|
||||
"User-Agent":"lowlighter/metrics",
|
||||
Accept:"application/json",
|
||||
}})).data.recenttracks.track.map(track => ({
|
||||
name:track.name,
|
||||
artist:track.artist["#text"],
|
||||
artwork:track.image.reverse()[0]["#text"],
|
||||
@@ -215,11 +231,11 @@
|
||||
track.artwork = await imports.imgb64(track.artwork)
|
||||
}
|
||||
//Save results
|
||||
return {...raw, tracks}
|
||||
return {...raw, tracks, played_at}
|
||||
}
|
||||
|
||||
//Unhandled error
|
||||
throw {error:{message:`An error occured (could not retrieve tracks)`}}
|
||||
throw {error:{message:"An error occured (could not retrieve tracks)"}}
|
||||
}
|
||||
//Handle errors
|
||||
catch (error) {
|
||||
@@ -227,4 +243,4 @@
|
||||
throw error
|
||||
throw {error:{message:"An error occured", instance:error}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ inputs:
|
||||
description: Embed playlist url
|
||||
type: string
|
||||
default: ""
|
||||
example: https://embed.music.apple.com/--/playlist/--------/--------
|
||||
|
||||
# Number of music tracks to display
|
||||
plugin_music_limit:
|
||||
@@ -55,6 +56,8 @@ inputs:
|
||||
default: 4
|
||||
min: 1
|
||||
max: 100
|
||||
|
||||
# Display when track was last played
|
||||
plugin_music_played_at:
|
||||
description: Display when the track was played
|
||||
type: boolean
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
token: NOT_NEEDED
|
||||
plugin_music_token: MOCKED_CLIENT_ID, MOCKED_CLIENT_SECRET, MOCKED_REFRESH_TOKEN
|
||||
plugin_music: yes
|
||||
plugin_music_played_at: yes
|
||||
plugin_music_provider: spotify
|
||||
|
||||
- name: Music plugin (recent - lastfm)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, imports, data, q, account}, {enabled = false, token = null} = {}) {
|
||||
export default async function({login, imports, data, q, account}, {enabled = false, token = null} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -53,4 +53,4 @@
|
||||
}
|
||||
throw {error:{message, instance:error}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, data, graphql, rest, q, queries, imports, account}, {enabled = false} = {}) {
|
||||
export default async function({login, data, graphql, rest, q, queries, imports, account}, {enabled = false} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -12,7 +12,7 @@
|
||||
types:account === "organization" ? ["sponsorshipsAsMaintainer", "sponsorshipsAsSponsor", "thanks"] : ["followers", "following", "sponsorshipsAsMaintainer", "sponsorshipsAsSponsor", "thanks"],
|
||||
default:"followers, following",
|
||||
alias:{followed:"following", sponsors:"sponsorshipsAsMaintainer", sponsored:"sponsorshipsAsSponsor", sponsoring:"sponsorshipsAsSponsor"},
|
||||
sponsorships:{sponsorshipsAsMaintainer:"sponsorEntity", sponsorshipsAsSponsor:"sponsorable"}
|
||||
sponsorships:{sponsorshipsAsMaintainer:"sponsorEntity", sponsorshipsAsSponsor:"sponsorable"},
|
||||
}
|
||||
if (q.repo) {
|
||||
console.debug(`metrics/compute/${login}/plugins > people > switched to repository mode`)
|
||||
|
||||
@@ -34,6 +34,7 @@ inputs:
|
||||
type: array
|
||||
format: comma-separated
|
||||
default: followers, following
|
||||
example: follower, following, sponsors, sponsoring
|
||||
values:
|
||||
- followers # For user metrics
|
||||
- following # For user metrics
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, data, imports, q, account}, {enabled = false} = {}) {
|
||||
export default async function({login, data, imports, q, account}, {enabled = false} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -36,7 +36,7 @@
|
||||
}
|
||||
|
||||
//Unhandled error
|
||||
throw {error:{message:`An error occured (could not retrieve posts)`}}
|
||||
throw {error:{message:"An error occured (could not retrieve posts)"}}
|
||||
}
|
||||
//Handle errors
|
||||
catch (error) {
|
||||
@@ -44,4 +44,4 @@
|
||||
throw error
|
||||
throw {error:{message:"An error occured", instance:error}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, data, imports, graphql, q, queries, account}, {enabled = false} = {}) {
|
||||
export default async function({login, data, imports, graphql, q, queries, account}, {enabled = false} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -21,7 +21,7 @@
|
||||
for (const identifier of repositories) {
|
||||
//Querying repository project
|
||||
console.debug(`metrics/compute/${login}/plugins > projects > querying api for ${identifier}`)
|
||||
const {user, repository, id} = identifier.match(/(?<user>[-\w]+)[/](?<repository>[-\w]+)[/]projects[/](?<id>\d+)/)?.groups
|
||||
const {user, repository, id} = identifier.match(/(?<user>[-\w]+)[/](?<repository>[-\w]+)[/]projects[/](?<id>\d+)/)?.groups ?? {}
|
||||
const {[account]:{repository:{project}}} = await graphql(queries.projects.repository({user, repository, id, account}))
|
||||
//Adding it to projects list
|
||||
console.debug(`metrics/compute/${login}/plugins > projects > registering ${identifier}`)
|
||||
|
||||
@@ -27,6 +27,7 @@ inputs:
|
||||
plugin_projects_repositories:
|
||||
description: List of repository project identifiers to disaplay
|
||||
type: array
|
||||
example: username/repo/projects/1, username/repo/projects/2, ...
|
||||
format:
|
||||
- comma-separated
|
||||
- /(?<user>[-a-z0-9]+)[/](?<repo>[-a-z0-9]+)[/]projects[/](?<id>[0-9]+)/
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, graphql, data, imports, q, queries, account}, {enabled = false} = {}) {
|
||||
export default async function({login, graphql, data, imports, q, queries, account}, {enabled = false} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -41,7 +41,7 @@
|
||||
increments.max = Math.max(...Object.values(increments.dates))
|
||||
|
||||
//Compute total stargazers
|
||||
let stargazers = data.computed.repositories.stargazers
|
||||
let {stargazers} = data.computed.repositories
|
||||
const total = {dates:{...increments.dates}, max:NaN, min:NaN}
|
||||
{
|
||||
const dates = Object.keys(total.dates)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, data, graphql, q, queries, imports, account}, {enabled = false} = {}) {
|
||||
export default async function({login, data, graphql, q, queries, imports, account}, {enabled = false} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, data, imports, q, account}, {enabled = false} = {}) {
|
||||
export default async function({login, data, imports, q, account}, {enabled = false} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -90,4 +90,4 @@
|
||||
throw error
|
||||
throw {error:{message:"An error occured", instance:error}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, imports, data, rest, q, account}, {enabled = false} = {}) {
|
||||
export default async function({login, imports, data, rest, q, account}, {enabled = false} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -15,7 +15,7 @@
|
||||
//Get views stats from repositories
|
||||
console.debug(`metrics/compute/${login}/plugins > traffic > querying api`)
|
||||
const views = {count:0, uniques:0}
|
||||
const response = await Promise.all(repositories.map(async ({repo, owner}) => await rest.repos.getViews({owner, repo})))
|
||||
const response = await Promise.all(repositories.map(({repo, owner}) => rest.repos.getViews({owner, repo})))
|
||||
|
||||
//Compute views
|
||||
console.debug(`metrics/compute/${login}/plugins > traffic > computing stats`)
|
||||
@@ -31,4 +31,4 @@
|
||||
message = "Insufficient token rights"
|
||||
throw {error:{message, instance:error}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, imports, data, q, account}, {enabled = false, token = ""} = {}) {
|
||||
export default async function({login, imports, data, q, account}, {enabled = false, token = ""} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -35,18 +35,17 @@
|
||||
tweet.mentions = tweet.entities?.mentions.map(({username}) => username) ?? []
|
||||
//Format text
|
||||
console.debug(`metrics/compute/${login}/plugins > tweets > formatting tweet ${tweet.id}`)
|
||||
tweet.text = imports.htmlescape(
|
||||
tweet.text = imports.htmlescape( //eslint-disable-line function-paren-newline
|
||||
//Escape tags
|
||||
imports.htmlescape(tweet.text, {"<":true, ">":true})
|
||||
//Mentions
|
||||
.replace(new RegExp(`@(${tweet.mentions.join("|")})`, "gi"), ` <span class="mention">@$1</span> `)
|
||||
.replace(new RegExp(`@(${tweet.mentions.join("|")})`, "gi"), ' <span class="mention">@$1</span> ')
|
||||
//Hashtags (this regex comes from the twitter source code)
|
||||
.replace(/(?<!&)[#|#]([a-z0-9_\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0100-\u024f\u0253-\u0254\u0256-\u0257\u0300-\u036f\u1e00-\u1eff\u0400-\u04ff\u0500-\u0527\u2de0-\u2dff\ua640-\ua69f\u0591-\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05d0-\u05ea\u05f0-\u05f4\ufb12-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4f\u0610-\u061a\u0620-\u065f\u066e-\u06d3\u06d5-\u06dc\u06de-\u06e8\u06ea-\u06ef\u06fa-\u06fc\u0750-\u077f\u08a2-\u08ac\u08e4-\u08fe\ufb50-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\u200c-\u200c\u0e01-\u0e3a\u0e40-\u0e4e\u1100-\u11ff\u3130-\u3185\ua960-\ua97f\uac00-\ud7af\ud7b0-\ud7ff\uffa1-\uffdc\u30a1-\u30fa\u30fc-\u30fe\uff66-\uff9f\uff10-\uff19\uff21-\uff3a\uff41-\uff5a\u3041-\u3096\u3099-\u309e\u3400-\u4dbf\u4e00-\u9fff\u20000-\u2a6df\u2a700-\u2b73f\u2b740-\u2b81f\u2f800-\u2fa1f]*[a-z_\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0100-\u024f\u0253-\u0254\u0256-\u0257\u0300-\u036f\u1e00-\u1eff\u0400-\u04ff\u0500-\u0527\u2de0-\u2dff\ua640-\ua69f\u0591-\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05d0-\u05ea\u05f0-\u05f4\ufb12-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4f\u0610-\u061a\u0620-\u065f\u066e-\u06d3\u06d5-\u06dc\u06de-\u06e8\u06ea-\u06ef\u06fa-\u06fc\u0750-\u077f\u08a2-\u08ac\u08e4-\u08fe\ufb50-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\u200c-\u200c\u0e01-\u0e3a\u0e40-\u0e4e\u1100-\u11ff\u3130-\u3185\ua960-\ua97f\uac00-\ud7af\ud7b0-\ud7ff\uffa1-\uffdc\u30a1-\u30fa\u30fc-\u30fe\uff66-\uff9f\uff10-\uff19\uff21-\uff3a\uff41-\uff5a\u3041-\u3096\u3099-\u309e\u3400-\u4dbf\u4e00-\u9fff\u20000-\u2a6df\u2a700-\u2b73f\u2b740-\u2b81f\u2f800-\u2fa1f][a-z0-9_\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0100-\u024f\u0253-\u0254\u0256-\u0257\u0300-\u036f\u1e00-\u1eff\u0400-\u04ff\u0500-\u0527\u2de0-\u2dff\ua640-\ua69f\u0591-\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05d0-\u05ea\u05f0-\u05f4\ufb12-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4f\u0610-\u061a\u0620-\u065f\u066e-\u06d3\u06d5-\u06dc\u06de-\u06e8\u06ea-\u06ef\u06fa-\u06fc\u0750-\u077f\u08a2-\u08ac\u08e4-\u08fe\ufb50-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\u200c-\u200c\u0e01-\u0e3a\u0e40-\u0e4e\u1100-\u11ff\u3130-\u3185\ua960-\ua97f\uac00-\ud7af\ud7b0-\ud7ff\uffa1-\uffdc\u30a1-\u30fa\u30fc-\u30fe\uff66-\uff9f\uff10-\uff19\uff21-\uff3a\uff41-\uff5a\u3041-\u3096\u3099-\u309e\u3400-\u4dbf\u4e00-\u9fff\u20000-\u2a6df\u2a700-\u2b73f\u2b740-\u2b81f\u2f800-\u2fa1f]*)/gi, ` <span class="hashtag">#$1</span> `)
|
||||
.replace(/(?<!&)[#|#]([a-z0-9_\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0100-\u024f\u0253-\u0254\u0256-\u0257\u0300-\u036f\u1e00-\u1eff\u0400-\u04ff\u0500-\u0527\u2de0-\u2dff\ua640-\ua69f\u0591-\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05d0-\u05ea\u05f0-\u05f4\ufb12-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4f\u0610-\u061a\u0620-\u065f\u066e-\u06d3\u06d5-\u06dc\u06de-\u06e8\u06ea-\u06ef\u06fa-\u06fc\u0750-\u077f\u08a2-\u08ac\u08e4-\u08fe\ufb50-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\u200c-\u200c\u0e01-\u0e3a\u0e40-\u0e4e\u1100-\u11ff\u3130-\u3185\ua960-\ua97f\uac00-\ud7af\ud7b0-\ud7ff\uffa1-\uffdc\u30a1-\u30fa\u30fc-\u30fe\uff66-\uff9f\uff10-\uff19\uff21-\uff3a\uff41-\uff5a\u3041-\u3096\u3099-\u309e\u3400-\u4dbf\u4e00-\u9fff\u20000-\u2a6df\u2a700-\u2b73f\u2b740-\u2b81f\u2f800-\u2fa1f]*[a-z_\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0100-\u024f\u0253-\u0254\u0256-\u0257\u0300-\u036f\u1e00-\u1eff\u0400-\u04ff\u0500-\u0527\u2de0-\u2dff\ua640-\ua69f\u0591-\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05d0-\u05ea\u05f0-\u05f4\ufb12-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4f\u0610-\u061a\u0620-\u065f\u066e-\u06d3\u06d5-\u06dc\u06de-\u06e8\u06ea-\u06ef\u06fa-\u06fc\u0750-\u077f\u08a2-\u08ac\u08e4-\u08fe\ufb50-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\u200c-\u200c\u0e01-\u0e3a\u0e40-\u0e4e\u1100-\u11ff\u3130-\u3185\ua960-\ua97f\uac00-\ud7af\ud7b0-\ud7ff\uffa1-\uffdc\u30a1-\u30fa\u30fc-\u30fe\uff66-\uff9f\uff10-\uff19\uff21-\uff3a\uff41-\uff5a\u3041-\u3096\u3099-\u309e\u3400-\u4dbf\u4e00-\u9fff\u20000-\u2a6df\u2a700-\u2b73f\u2b740-\u2b81f\u2f800-\u2fa1f][a-z0-9_\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u00ff\u0100-\u024f\u0253-\u0254\u0256-\u0257\u0300-\u036f\u1e00-\u1eff\u0400-\u04ff\u0500-\u0527\u2de0-\u2dff\ua640-\ua69f\u0591-\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05d0-\u05ea\u05f0-\u05f4\ufb12-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4f\u0610-\u061a\u0620-\u065f\u066e-\u06d3\u06d5-\u06dc\u06de-\u06e8\u06ea-\u06ef\u06fa-\u06fc\u0750-\u077f\u08a2-\u08ac\u08e4-\u08fe\ufb50-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\u200c-\u200c\u0e01-\u0e3a\u0e40-\u0e4e\u1100-\u11ff\u3130-\u3185\ua960-\ua97f\uac00-\ud7af\ud7b0-\ud7ff\uffa1-\uffdc\u30a1-\u30fa\u30fc-\u30fe\uff66-\uff9f\uff10-\uff19\uff21-\uff3a\uff41-\uff5a\u3041-\u3096\u3099-\u309e\u3400-\u4dbf\u4e00-\u9fff\u20000-\u2a6df\u2a700-\u2b73f\u2b740-\u2b81f\u2f800-\u2fa1f]*)/gi, ' <span class="hashtag">#$1</span> ') //eslint-disable-line no-misleading-character-class, prefer-named-capture-group
|
||||
//Line breaks
|
||||
.replace(/\n/g, "<br/>")
|
||||
//Links
|
||||
.replace(/https?:[/][/](t.co[/]\w+)/g, ` <span class="link">$1</span> `)
|
||||
, {"&":true})
|
||||
.replace(/https?:[/][/](?<link>t.co[/]\w+)/g, ' <span class="link">$<link></span> '), {"&":true})
|
||||
}))
|
||||
|
||||
//Result
|
||||
@@ -63,4 +62,4 @@
|
||||
}
|
||||
throw {error:{message, instance:error}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//Setup
|
||||
export default async function ({login, q, imports, data, account}, {enabled = false, token} = {}) {
|
||||
export default async function({login, q, imports, data, account}, {enabled = false, token} = {}) {
|
||||
//Plugin execution
|
||||
try {
|
||||
//Check if plugin is enabled and requirements are met
|
||||
@@ -12,8 +12,7 @@
|
||||
limit = void(limit)
|
||||
const range = {"7":"last_7_days", "30":"last_30_days", "180":"last_6_months", "365":"last_year"}[days] ?? "last_7_days"
|
||||
|
||||
//Querying api and format result
|
||||
//https://wakatime.com/developers#stats
|
||||
//Querying api and format result (https://wakatime.com/developers#stats)
|
||||
console.debug(`metrics/compute/${login}/plugins > wakatime > querying api`)
|
||||
const {data:{data:stats}} = await imports.axios.get(`https://wakatime.com/api/v1/users/current/stats/${range}?api_key=${token}`)
|
||||
const result = {
|
||||
@@ -42,4 +41,4 @@
|
||||
}
|
||||
throw {error:{message, instance:error}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user