forked from MapComplete/MastodonBot
Fix mastodon bot to work with panoramax
This commit is contained in:
parent
ddd8566efc
commit
570dacb89e
5 changed files with 87 additions and 47 deletions
63
package-lock.json
generated
63
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "mastodon-bot",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.3",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mastodon-bot",
|
||||
"version": "0.0.1",
|
||||
"version": "0.0.3",
|
||||
"license": "GPL",
|
||||
"dependencies": {
|
||||
"@types/node-fetch": "^2.6.2",
|
||||
|
@ -20,6 +20,7 @@
|
|||
"masto": "^5.4.0",
|
||||
"mocha": "^10.0.0",
|
||||
"node-fetch": "^3.3.0",
|
||||
"panoramax-js": "^0.3.7",
|
||||
"showdown": "^2.1.0",
|
||||
"ts-node": "^10.7.0"
|
||||
},
|
||||
|
@ -227,6 +228,19 @@
|
|||
"webidl-conversions": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ogcapi-js/features": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@ogcapi-js/features/-/features-1.1.1.tgz",
|
||||
"integrity": "sha512-/w6kFvAXWO+F0/nLC5m11tuOw0LX+gVz/OCLiDkElXO9ko9F9OA3AbzKZxJaE5Buu0KUGn+TRxS6w1xhZc4KRA==",
|
||||
"dependencies": {
|
||||
"@ogcapi-js/shared": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@ogcapi-js/shared": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@ogcapi-js/shared/-/shared-1.1.1.tgz",
|
||||
"integrity": "sha512-EQ6T4iVXwIMnBcdpR2C0YnNNCxtNWHpWg0Hs9uEvH4BPZI2xT87gV+WRw8/hYAe8EtrK6j57iluBoSyHiAQweQ=="
|
||||
},
|
||||
"node_modules/@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
|
@ -252,6 +266,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz",
|
||||
"integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw=="
|
||||
},
|
||||
"node_modules/@types/geojson": {
|
||||
"version": "7946.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz",
|
||||
"integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg=="
|
||||
},
|
||||
"node_modules/@types/mocha": {
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz",
|
||||
|
@ -1935,6 +1954,17 @@
|
|||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/panoramax-js": {
|
||||
"version": "0.3.7",
|
||||
"resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.3.7.tgz",
|
||||
"integrity": "sha512-jkmHPaIsjfalwGG2CZxe24t4F8sY7phOFz5+nC4VnOY4T+6peXMLqZZ4u6d/pOmkf5OD2hk2mFL4pO1viLoWBA==",
|
||||
"dependencies": {
|
||||
"@ogcapi-js/features": "^1.1.1",
|
||||
"@ogcapi-js/shared": "^1.1.1",
|
||||
"@types/geojson": "^7946.0.14",
|
||||
"json-schema": "^0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/param-case": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
|
||||
|
@ -3041,6 +3071,19 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"@ogcapi-js/features": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@ogcapi-js/features/-/features-1.1.1.tgz",
|
||||
"integrity": "sha512-/w6kFvAXWO+F0/nLC5m11tuOw0LX+gVz/OCLiDkElXO9ko9F9OA3AbzKZxJaE5Buu0KUGn+TRxS6w1xhZc4KRA==",
|
||||
"requires": {
|
||||
"@ogcapi-js/shared": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"@ogcapi-js/shared": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@ogcapi-js/shared/-/shared-1.1.1.tgz",
|
||||
"integrity": "sha512-EQ6T4iVXwIMnBcdpR2C0YnNNCxtNWHpWg0Hs9uEvH4BPZI2xT87gV+WRw8/hYAe8EtrK6j57iluBoSyHiAQweQ=="
|
||||
},
|
||||
"@tsconfig/node10": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz",
|
||||
|
@ -3066,6 +3109,11 @@
|
|||
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz",
|
||||
"integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw=="
|
||||
},
|
||||
"@types/geojson": {
|
||||
"version": "7946.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.14.tgz",
|
||||
"integrity": "sha512-WCfD5Ht3ZesJUsONdhvm84dmzWOiOzOAqOncN0++w0lBw1o8OuDNJF2McvvCef/yBqb/HYRahp1BYtODFQ8bRg=="
|
||||
},
|
||||
"@types/mocha": {
|
||||
"version": "9.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz",
|
||||
|
@ -4366,6 +4414,17 @@
|
|||
"p-limit": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"panoramax-js": {
|
||||
"version": "0.3.7",
|
||||
"resolved": "https://registry.npmjs.org/panoramax-js/-/panoramax-js-0.3.7.tgz",
|
||||
"integrity": "sha512-jkmHPaIsjfalwGG2CZxe24t4F8sY7phOFz5+nC4VnOY4T+6peXMLqZZ4u6d/pOmkf5OD2hk2mFL4pO1viLoWBA==",
|
||||
"requires": {
|
||||
"@ogcapi-js/features": "^1.1.1",
|
||||
"@ogcapi-js/shared": "^1.1.1",
|
||||
"@types/geojson": "^7946.0.14",
|
||||
"json-schema": "^0.4.0"
|
||||
}
|
||||
},
|
||||
"param-case": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
"masto": "^5.4.0",
|
||||
"mocha": "^10.0.0",
|
||||
"node-fetch": "^3.3.0",
|
||||
"panoramax-js": "^0.3.7",
|
||||
"showdown": "^2.1.0",
|
||||
"ts-node": "^10.7.0"
|
||||
},
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import {ChangeSetData} from "./OsmCha";
|
||||
import MastodonPoster from "./Mastodon";
|
||||
import OsmUserInfo from "./OsmUserInfo";
|
||||
import ImgurAttribution from "./ImgurAttribution";
|
||||
import Utils from "./Utils";
|
||||
import Config from "./Config";
|
||||
import {ImageData, Panoramax, PanoramaxXYZ} from "panoramax-js";
|
||||
|
||||
export default class ImageUploader {
|
||||
private readonly _imageQueue: { image: string; changeset: ChangeSetData }[];
|
||||
|
@ -13,31 +13,30 @@ export default class ImageUploader {
|
|||
private readonly _globalConfig: Config
|
||||
|
||||
|
||||
|
||||
constructor(imageQueue: {image: string, changeset: ChangeSetData}[], poster: MastodonPoster, config: Config) {
|
||||
constructor(imageQueue: { image: string, changeset: ChangeSetData }[], poster: MastodonPoster, config: Config) {
|
||||
this._imageQueue = imageQueue;
|
||||
this._poster = poster;
|
||||
this._globalConfig = config
|
||||
}
|
||||
|
||||
public getCurrentAuthors(){
|
||||
public getCurrentAuthors() {
|
||||
return [...this._authors]
|
||||
}
|
||||
|
||||
public async attemptToUpload(targetcount: number): Promise<string[]>{
|
||||
public async attemptToUpload(targetcount: number): Promise<string[]> {
|
||||
const mediaIds = []
|
||||
while(mediaIds.length < targetcount && this._imageQueue.length >0){
|
||||
while (mediaIds.length < targetcount && this._imageQueue.length > 0) {
|
||||
const first = this._imageQueue[0]
|
||||
try {
|
||||
const id = await this.uploadFirstImage()
|
||||
mediaIds.push(id)
|
||||
}catch (e) {
|
||||
} catch (e) {
|
||||
console.error("Could not upload image! ", first.image, e)
|
||||
console.log("Trying again")
|
||||
try {
|
||||
const id = await this.uploadFirstImage()
|
||||
mediaIds.push(id)
|
||||
}catch (e) {
|
||||
} catch (e) {
|
||||
console.error("Retry could not upload image! ", first.image, e)
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +44,7 @@ export default class ImageUploader {
|
|||
return mediaIds
|
||||
}
|
||||
|
||||
private async uploadFirstImage(): Promise<string>{
|
||||
private async uploadFirstImage(): Promise<string> {
|
||||
const image = this._imageQueue.shift()
|
||||
const cs = image.changeset.properties
|
||||
let authorName = cs.user
|
||||
|
@ -61,11 +60,20 @@ export default class ImageUploader {
|
|||
return "dummy_id"
|
||||
}
|
||||
console.log("Fetching attribution for", image.image)
|
||||
const attribution = await ImgurAttribution.DownloadAttribution(image.image)
|
||||
const id = image.image.substring(image.image.lastIndexOf("/") + 1)
|
||||
const path = this._globalConfig.cacheDir + "/image_" + id
|
||||
await Utils.DownloadBlob(image.image, path)
|
||||
const mediaId = await this._poster.uploadImage(path, "Image taken by " + authorName + ", available under " + attribution.license + ". It is made with the thematic map " + image.changeset.properties.theme + " in changeset https://openstreetmap.org/changeset/" + image.changeset.id)
|
||||
let imageData: ImageData = undefined
|
||||
try {
|
||||
|
||||
const p = new Panoramax("https://panoramax.mapcomplete.org")
|
||||
imageData = await p.imageInfo(image.image)
|
||||
} catch (e) {
|
||||
const p = new PanoramaxXYZ()
|
||||
imageData = await p.imageInfo(image.image)
|
||||
|
||||
}
|
||||
const path = this._globalConfig.cacheDir + "/image_" + image.image
|
||||
console.log("Fetching image:", imageData.assets.sd.href)
|
||||
await Utils.DownloadBlob(imageData.assets.sd.href, path)
|
||||
const mediaId = await this._poster.uploadImage(path, "Image taken by " + authorName + ", available under " + imageData.properties["geovisio:license"] + ". It is made with the thematic map " + image.changeset.properties.theme + " in changeset https://openstreetmap.org/changeset/" + image.changeset.id)
|
||||
|
||||
this._authors.push(authorName)
|
||||
return mediaId
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
import Utils from "./Utils";
|
||||
|
||||
export default class ImgurAttribution {
|
||||
|
||||
// MUST be private to prevent other people stealing this key! That I'll push this to github later on is not relevant
|
||||
private static ImgurApiKey = "7070e7167f0a25a"
|
||||
|
||||
/**
|
||||
* Download the attribution from a given URL
|
||||
*/
|
||||
public static async DownloadAttribution(url: string): Promise<{license: string, author: string}> {
|
||||
const hash = url.substr("https://i.imgur.com/".length).split(/.jpe?g/i)[0]
|
||||
|
||||
const apiUrl = "https://api.imgur.com/3/image/" + hash
|
||||
const response = await Utils.DownloadJson(apiUrl, {
|
||||
Authorization: "Client-ID " + ImgurAttribution.ImgurApiKey,
|
||||
})
|
||||
|
||||
const descr: string = response.data.description ?? ""
|
||||
const data: any = {}
|
||||
for (const tag of descr.split("\n")) {
|
||||
const kv = tag.split(":")
|
||||
const k = kv[0]
|
||||
data[k] = kv[1]?.replace(/\r/g, "")
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
|
@ -441,7 +441,7 @@ export class Postbuilder {
|
|||
const osmChangeset = await Utils.DownloadXml(url)
|
||||
const osmChangesetTags: { k: string, v: string }[] = Array.from(osmChangeset.getElementsByTagName("tag"))
|
||||
.map(tag => ({k: tag.getAttribute("k"), v: tag.getAttribute("v")}))
|
||||
.filter(kv => kv.k.startsWith("image"))
|
||||
.filter(kv => kv.k.startsWith("panoramax"))
|
||||
|
||||
for (const kv of osmChangesetTags) {
|
||||
if (seenURLS.has(kv.v)) {
|
||||
|
|
Loading…
Reference in a new issue