MastodonBot/src/OsmUserInfo.ts

119 lines
No EOL
4.3 KiB
TypeScript

import Utils from "./Utils";
import * as fs from "fs";
import MastodonPoster from "./Mastodon";
export interface UserInfo {
"id": number,
"display_name": string,
"account_created": string,
"description": string,
"contributor_terms": { "agreed": boolean, "pd": boolean },
"img": { "href": string },
"roles": [],
"changesets": { "count": number },
"traces": { "count": number },
"blocks": { "received": { "count": number, "active": number } },
}
export default class OsmUserInfo {
private static readonly max_cache_age_seconds = 7 * 24 * 60 * 60;
private readonly _userId: number;
private readonly _backend: string;
private _userData: UserInfo = undefined
private readonly _cachingPath: string | undefined;
constructor(userId: number, options?:
{
osmBackend?: string,
cacheDir?: string
}) {
if(userId === undefined || userId === null || Number.isNaN(userId)){
throw new Error("Invalid userid: " + userId)
}
this._userId = userId;
this._backend = options?.osmBackend ?? "https://www.openstreetmap.org/";
if (options?.cacheDir) {
this._cachingPath = options?.cacheDir + "/userinfo_" + userId + ".json"
}
if (!this._backend.endsWith("/")) {
this._backend += "/"
}
}
public async hasNoBotTag(): Promise<{
nobot: boolean,
nomention: boolean
}>{
const description = (await this.getUserInfo()).description ?? ""
const split = description.toLowerCase().replace(/-/g, "").split(" ")
const nobot = split.indexOf("#nobot") >=0 || split.indexOf("#nomapcompletebot") >= 0
const nomention = split.indexOf("#nobotmention") >=0 || split.indexOf("#nomapcompletebotmention") >= 0
return {nobot, nomention}
}
/**
* Gets the Mastodon username of the this OSM-user to ping them.
* @param mastodonApi: will be used to lookup the metadata of the user; if they have '#nobot' in their bio, don't mention them
* @constructor
*/
public async GetMastodonUsername(mastodonApi: MastodonPoster): Promise<string | undefined> {
const {nomention} = await this.hasNoBotTag()
if(nomention){
return undefined
}
const mastodonLinks = await this.getMeLinks()
if (mastodonLinks.length <= 0) {
return undefined
}
const url = new URL(mastodonLinks[0])
const username = url.pathname.substring(1) + "@" + url.host
if(await mastodonApi.hasNoBot(username)){
return undefined
}
const useraccount = await mastodonApi.userInfoFor(username)
return useraccount.acct
}
public async getMeLinks(): Promise<string[]> {
const userdata = await this.getUserInfo()
const div = document.createElement("div")
div.innerHTML = userdata.description
const links = Array.from(div.getElementsByTagName("a"))
const meLinks = links.filter(link => link.getAttribute("rel")?.split(" ")?.indexOf("me") >= 0)
return meLinks.map(link => link.href.toString())
}
public async getUserInfo(): Promise<UserInfo> {
if (this._userData) {
return this._userData
}
if (this._cachingPath !== undefined && fs.existsSync(this._cachingPath)) {
const cacheCreatedTime: Date = fs.statSync(this._cachingPath).birthtime
const cacheAgeInSeconds = (Date.now() - cacheCreatedTime.getTime()) / 1000
if (cacheAgeInSeconds > OsmUserInfo.max_cache_age_seconds) {
console.log("Cache is old, unlinking...")
}else{
try {
this._userData = JSON.parse(fs.readFileSync(this._cachingPath, "utf8"))
return this._userData
} catch (e) {
fs.unlinkSync(this._cachingPath)
}
}
}
const url = `${this._backend}api/0.6/user/${this._userId}.json`
console.log("Looking up OSM user info about ", this._userId)
const res = await Utils.DownloadJson(url);
this._userData = res.user
if (this._cachingPath !== undefined) {
fs.writeFileSync(this._cachingPath, JSON.stringify(this._userData), "utf8")
}
return this._userData
}
}