mirror of
https://github.com/turt2live/matrix-dimension.git
synced 2024-09-28 19:55:41 +00:00
Support custom stickerpacks
This commit is contained in:
parent
968fb18a57
commit
147ef2104e
30
package-lock.json
generated
30
package-lock.json
generated
@ -5659,7 +5659,8 @@
|
||||
"ip-regex": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
|
||||
"integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk="
|
||||
"integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=",
|
||||
"dev": true
|
||||
},
|
||||
"ipaddr.js": {
|
||||
"version": "1.8.0",
|
||||
@ -11078,22 +11079,22 @@
|
||||
}
|
||||
},
|
||||
"request-promise": {
|
||||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.2.tgz",
|
||||
"integrity": "sha1-0epG1lSm7k+O5qT+oQGMIpEZBLQ=",
|
||||
"version": "4.2.4",
|
||||
"resolved": "https://registry.npmjs.org/request-promise/-/request-promise-4.2.4.tgz",
|
||||
"integrity": "sha512-8wgMrvE546PzbR5WbYxUQogUnUDfM0S7QIFZMID+J73vdFARkFy+HElj4T+MWYhpXwlLp0EQ8Zoj8xUA0he4Vg==",
|
||||
"requires": {
|
||||
"bluebird": "^3.5.0",
|
||||
"request-promise-core": "1.1.1",
|
||||
"stealthy-require": "^1.1.0",
|
||||
"tough-cookie": ">=2.3.3"
|
||||
"request-promise-core": "1.1.2",
|
||||
"stealthy-require": "^1.1.1",
|
||||
"tough-cookie": "^2.3.3"
|
||||
}
|
||||
},
|
||||
"request-promise-core": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz",
|
||||
"integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=",
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz",
|
||||
"integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==",
|
||||
"requires": {
|
||||
"lodash": "^4.13.1"
|
||||
"lodash": "^4.17.11"
|
||||
}
|
||||
},
|
||||
"require-dir-all": {
|
||||
@ -12978,11 +12979,10 @@
|
||||
"integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg="
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz",
|
||||
"integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==",
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
||||
"requires": {
|
||||
"ip-regex": "^2.1.0",
|
||||
"psl": "^1.1.28",
|
||||
"punycode": "^2.1.1"
|
||||
},
|
||||
|
@ -38,7 +38,7 @@
|
||||
"netmask": "^1.0.6",
|
||||
"random-string": "^0.2.0",
|
||||
"request": "^2.88.0",
|
||||
"request-promise": "^4.2.2",
|
||||
"request-promise": "^4.2.4",
|
||||
"require-dir-all": "^0.4.15",
|
||||
"sequelize": "^4.39.1",
|
||||
"sequelize-typescript": "^0.6.6",
|
||||
|
@ -5,6 +5,8 @@ import Sticker from "../../db/models/Sticker";
|
||||
import { ScalarService } from "../scalar/ScalarService";
|
||||
import UserStickerPack from "../../db/models/UserStickerPack";
|
||||
import { ApiError } from "../ApiError";
|
||||
import { StickerpackMetadataDownloader } from "../../utils/StickerpackMetadataDownloader";
|
||||
import { MatrixStickerBot } from "../../matrix/MatrixStickerBot";
|
||||
|
||||
export interface MemoryStickerPack {
|
||||
id: number;
|
||||
@ -35,6 +37,7 @@ export interface MemoryStickerPack {
|
||||
height: number;
|
||||
};
|
||||
}[];
|
||||
trackingRoomAlias: string;
|
||||
}
|
||||
|
||||
export interface MemoryUserStickerPack extends MemoryStickerPack {
|
||||
@ -45,6 +48,10 @@ interface SetSelectedRequest {
|
||||
isSelected: boolean;
|
||||
}
|
||||
|
||||
interface ImportPackRequest {
|
||||
packUrl: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* API for stickers
|
||||
*/
|
||||
@ -88,6 +95,7 @@ export class DimensionStickerService {
|
||||
|
||||
const selectedPack = JSON.parse(JSON.stringify(pack));
|
||||
selectedPack.isSelected = userPack ? userPack.isSelected : false;
|
||||
if (!selectedPack.isSelected && pack.trackingRoomAlias) continue;
|
||||
packs.push(selectedPack);
|
||||
}
|
||||
|
||||
@ -119,6 +127,28 @@ export class DimensionStickerService {
|
||||
return {}; // 200 OK
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("packs/import")
|
||||
public async importPack(@QueryParam("scalar_token") scalarToken: string, request: ImportPackRequest): Promise<MemoryUserStickerPack> {
|
||||
await ScalarService.getTokenOwner(scalarToken);
|
||||
|
||||
const packUrl = request.packUrl.endsWith(".json") ? request.packUrl : `${request.packUrl}.json`;
|
||||
const metadata = await StickerpackMetadataDownloader.getMetadata(packUrl);
|
||||
await MatrixStickerBot.trackStickerpack(metadata.roomAlias);
|
||||
|
||||
const stickerPacks = await StickerPack.findAll({where: {trackingRoomAlias: metadata.roomAlias}});
|
||||
Cache.for(CACHE_STICKERS).clear();
|
||||
|
||||
if (stickerPacks.length <= 0) throw new ApiError(500, "Stickerpack not imported");
|
||||
const pack = stickerPacks[0];
|
||||
|
||||
// Simulate a call to setPackSelected
|
||||
await this.setPackSelected(scalarToken, pack.id, {isSelected: true});
|
||||
|
||||
const memoryPack = await DimensionStickerService.packToMemory(pack);
|
||||
return Object.assign({isSelected: true}, memoryPack);
|
||||
}
|
||||
|
||||
public static async packToMemory(pack: StickerPack): Promise<MemoryStickerPack> {
|
||||
const stickers = await Sticker.findAll({where: {packId: pack.id}});
|
||||
return {
|
||||
@ -152,6 +182,7 @@ export class DimensionStickerService {
|
||||
},
|
||||
}
|
||||
}),
|
||||
trackingRoomAlias: pack.trackingRoomAlias,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,16 @@
|
||||
import { QueryInterface } from "sequelize";
|
||||
import { DataType } from "sequelize-typescript";
|
||||
|
||||
export default {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return Promise.resolve()
|
||||
.then(() => queryInterface.addColumn("dimension_sticker_packs", "trackingRoomAlias", {
|
||||
type: DataType.STRING,
|
||||
allowNull: true
|
||||
}));
|
||||
},
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return Promise.resolve()
|
||||
.then(() => queryInterface.removeColumn("dimension_sticker_packs", "trackingRoomAlias"));
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { AutoIncrement, Column, Model, PrimaryKey, Table } from "sequelize-typescript";
|
||||
import { AllowNull, AutoIncrement, Column, Model, PrimaryKey, Table } from "sequelize-typescript";
|
||||
import { IntegrationRecord } from "./IntegrationRecord";
|
||||
|
||||
@Table({
|
||||
@ -44,4 +44,8 @@ export default class StickerPack extends Model<StickerPack> implements Integrati
|
||||
|
||||
@Column
|
||||
licensePath: string;
|
||||
|
||||
@AllowNull
|
||||
@Column
|
||||
trackingRoomAlias: string;
|
||||
}
|
@ -6,10 +6,14 @@ import {
|
||||
} from "matrix-bot-sdk";
|
||||
import config from "../config";
|
||||
import { LogService } from "matrix-js-snippets";
|
||||
import StickerPack from "../db/models/StickerPack";
|
||||
import Sticker from "../db/models/Sticker";
|
||||
import { MatrixLiteClient } from "./MatrixLiteClient";
|
||||
import { Cache, CACHE_STICKERS } from "../MemoryCache";
|
||||
|
||||
class _MatrixStickerBot {
|
||||
|
||||
private client: MatrixClient;
|
||||
private readonly client: MatrixClient;
|
||||
|
||||
constructor() {
|
||||
this.client = new MatrixClient(
|
||||
@ -19,7 +23,6 @@ class _MatrixStickerBot {
|
||||
|
||||
this.client.setJoinStrategy(new SimpleRetryJoinStrategy());
|
||||
this.client.on("room.event", this.onEvent.bind(this));
|
||||
this.client.on("room.upgraded", this.onUpgraded.bind(this));
|
||||
AutojoinUpgradedRoomsMixin.setupOnClient(this.client);
|
||||
}
|
||||
|
||||
@ -31,12 +34,109 @@ class _MatrixStickerBot {
|
||||
return this.client.getUserId();
|
||||
}
|
||||
|
||||
private onEvent(roomId, event) {
|
||||
private async onEvent(roomId, event) {
|
||||
LogService.info("MatrixStickerBot", `Event ${event.type} in ${roomId}`);
|
||||
if (event.type !== "io.t2bot.stickers.metadata" || event.state_key !== "") return;
|
||||
|
||||
const canonicalAlias = await this.client.getRoomStateEvent(roomId, "m.room.canonical_alias", "");
|
||||
const stickerPacks = await StickerPack.findAll({where: {trackingRoomAlias: canonicalAlias.alias}});
|
||||
|
||||
if (stickerPacks.length > 0) {
|
||||
return this.updateStickersInPacks(stickerPacks, roomId);
|
||||
}
|
||||
}
|
||||
|
||||
private onUpgraded(roomId, event) {
|
||||
LogService.info("MatrixStickerBot", `Room ${roomId} upgraded due to ${event.type}`);
|
||||
public trackStickerpack(alias: string): Promise<any> {
|
||||
return this.client.joinRoom(alias).then(async (roomId) => {
|
||||
const stickerPacks = await StickerPack.findAll({where: {trackingRoomAlias: alias}});
|
||||
if (stickerPacks.length > 0) {
|
||||
return this.updateStickersInPacks(stickerPacks, roomId);
|
||||
} else {
|
||||
const pack = await StickerPack.create({
|
||||
type: "stickerpack",
|
||||
name: "PLACEHOLDER",
|
||||
description: "PLACEHOLDER",
|
||||
avatarUrl: "mxc://localhost/NotYetLoaded",
|
||||
isEnabled: false,
|
||||
isPublic: true,
|
||||
authorType: "matrix",
|
||||
authorName: await this.getUserId(),
|
||||
authorReference: "https://matrix.to/#/" + (await this.getUserId()),
|
||||
license: "Imported",
|
||||
licensePath: "/licenses/general-imported.txt",
|
||||
trackingRoomAlias: alias,
|
||||
});
|
||||
return this.updateStickersInPacks([pack], roomId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async updateStickersInPacks(stickerPacks: StickerPack[], roomId: string) {
|
||||
const nameEvent = await this.client.getRoomStateEvent(roomId, "m.room.name", "");
|
||||
if (!nameEvent) return null;
|
||||
|
||||
const canconicalAliasEvent = await this.client.getRoomStateEvent(roomId, "m.room.canonical_alias", "");
|
||||
if (!canconicalAliasEvent) return null;
|
||||
|
||||
const packEvent = await this.client.getRoomStateEvent(roomId, "io.t2bot.stickers.metadata", "");
|
||||
if (!packEvent) return null;
|
||||
|
||||
let authorDisplayName = packEvent.creatorId;
|
||||
try {
|
||||
const profile = await this.client.getUserProfile(packEvent.creatorId);
|
||||
if (profile && profile.displayname) authorDisplayName = profile.displayname;
|
||||
} catch (e) {
|
||||
LogService.warn("MatrixStickerBot", e);
|
||||
}
|
||||
|
||||
const mx = new MatrixLiteClient(config.homeserver.accessToken);
|
||||
|
||||
const stickerEvents = [];
|
||||
for (const stickerId of packEvent.activeStickers) {
|
||||
const stickerEvent = await this.client.getRoomStateEvent(roomId, "io.t2bot.stickers.sticker", stickerId);
|
||||
if (!stickerEvent) continue;
|
||||
|
||||
const mxc = stickerEvent.contentUri;
|
||||
const serverName = mxc.substring("mxc://".length).split("/")[0];
|
||||
const contentId = mxc.substring("mxc://".length).split("/")[1];
|
||||
stickerEvent.thumbMxc = await mx.uploadFromUrl(await mx.getThumbnailUrl(serverName, contentId, 512, 512, "scale", false), "image/png");
|
||||
|
||||
stickerEvents.push(stickerEvent);
|
||||
}
|
||||
|
||||
|
||||
for (const pack of stickerPacks) {
|
||||
pack.isEnabled = true;
|
||||
pack.authorType = "matrix";
|
||||
pack.authorReference = "https://matrix.to/#/" + packEvent.creatorId;
|
||||
pack.authorName = authorDisplayName;
|
||||
pack.trackingRoomAlias = canconicalAliasEvent.alias;
|
||||
pack.name = nameEvent.name;
|
||||
pack.description = "Matrix sticker pack created by " + authorDisplayName;
|
||||
pack.license = "Imported";
|
||||
pack.licensePath = "/licenses/general-imported.txt";
|
||||
if (stickerEvents.length > 0) pack.avatarUrl = stickerEvents[0].contentUri;
|
||||
await pack.save();
|
||||
|
||||
const existingStickers = await Sticker.findAll({where: {packId: pack.id}});
|
||||
for (const sticker of existingStickers) await sticker.destroy();
|
||||
|
||||
for (const stickerEvent of stickerEvents) {
|
||||
await Sticker.create({
|
||||
packId: pack.id,
|
||||
name: stickerEvent.description,
|
||||
description: stickerEvent.description,
|
||||
imageMxc: stickerEvent.contentUri,
|
||||
thumbnailMxc: stickerEvent.thumbMxc,
|
||||
thumbnailWidth: 512,
|
||||
thumbnailHeight: 512,
|
||||
mimetype: "image/png",
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
LogService.info("MatrixStickerBot", `Updated ${stickerPacks.length} stickerpacks`);
|
||||
Cache.for(CACHE_STICKERS).clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
25
src/utils/StickerpackMetadataDownloader.ts
Normal file
25
src/utils/StickerpackMetadataDownloader.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import * as request from "request-promise";
|
||||
|
||||
export interface StickerpackMetadata {
|
||||
creatorId: string;
|
||||
roomId: string;
|
||||
roomAlias: string;
|
||||
}
|
||||
|
||||
export class StickerpackMetadataDownloader {
|
||||
public static getMetadata(packUrl: string): Promise<StickerpackMetadata> {
|
||||
return request({
|
||||
uri: packUrl,
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
json: true,
|
||||
}).then(body => {
|
||||
if (!body || !body["io.t2bot.dimension"]) {
|
||||
throw new Error("Failed to locate Dimension metadata");
|
||||
}
|
||||
|
||||
return body["io.t2bot.dimension"];
|
||||
});
|
||||
}
|
||||
}
|
@ -2,11 +2,30 @@
|
||||
<my-spinner></my-spinner>
|
||||
</div>
|
||||
<div *ngIf="!isLoading">
|
||||
<my-ibox title="Sticker Packs">
|
||||
<div class="my-ibox-content" *ngIf="packs.length <= 0">
|
||||
<my-ibox title="Sticker Packs" *ngIf="packs.length <= 0">
|
||||
<div class="my-ibox-content">
|
||||
<h5 style="text-align: center;">Sticker packs are not enabled on this Dimension instance.</h5>
|
||||
</div>
|
||||
<div class="my-ibox-content" *ngIf="packs.length > 0">
|
||||
</my-ibox>
|
||||
<my-ibox title="Add Sticker Packs" *ngIf="packs.length > 0">
|
||||
<div class="my-ibox-content">
|
||||
<form (submit)="importPack()" novalidate name="importForm">
|
||||
<label class="label-block">
|
||||
Stickerpack URL
|
||||
<input type="text" class="form-control" name="packUrl"
|
||||
placeholder="https://packs.t2bot.io/pack/..."
|
||||
[(ngModel)]="packUrl" [disabled]="isImporting"/>
|
||||
</label>
|
||||
<div style="margin-top: 25px">
|
||||
<button type="submit" class="btn btn-sm btn-success" [disabled]="isImporting">
|
||||
<i class="fa fa-plus"></i> Add stickerpack
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</my-ibox>
|
||||
<my-ibox title="Sticker Packs" *ngIf="packs.length > 0">
|
||||
<div class="my-ibox-content">
|
||||
<div class="pack" *ngFor="let pack of packs trackById">
|
||||
<img [src]="getThumbnailUrl(pack.avatarUrl, 120, 120)" width="120" height="120"/>
|
||||
<div class="caption">
|
||||
|
@ -16,6 +16,10 @@ export class StickerpickerComponent implements OnInit {
|
||||
public isUpdating = false;
|
||||
public packs: FE_UserStickerPack[];
|
||||
|
||||
// Import stuff
|
||||
public packUrl = "";
|
||||
public isImporting = false;
|
||||
|
||||
constructor(private stickerApi: StickerApiService,
|
||||
private media: MediaService,
|
||||
private scalarClient: ScalarClientApiService,
|
||||
@ -35,6 +39,21 @@ export class StickerpickerComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
public importPack() {
|
||||
this.isImporting = true;
|
||||
this.stickerApi.importStickerpack(this.packUrl).then(pack => {
|
||||
// Insert at top for visibility
|
||||
this.packs.splice(0, 0, pack);
|
||||
this.packUrl = "";
|
||||
this.isImporting = false;
|
||||
this.toaster.pop("success", "Stickerpack added");
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
this.isImporting = false;
|
||||
this.toaster.pop("error", "Error adding stickerpack");
|
||||
});
|
||||
}
|
||||
|
||||
public getThumbnailUrl(mxc: string, width: number, height: number, method: "crop" | "scale" = "scale"): string {
|
||||
return this.media.getThumbnailUrl(mxc, width, height, method, true);
|
||||
}
|
||||
|
@ -16,4 +16,8 @@ export class StickerApiService extends AuthedApi {
|
||||
public togglePackSelection(packId: number, isSelected: boolean): Promise<any> {
|
||||
return this.authedPost("/api/v1/dimension/stickers/packs/" + packId + "/selected", {isSelected: isSelected}).map(r => r.json()).toPromise();
|
||||
}
|
||||
|
||||
public importStickerpack(packUrl: string): Promise<FE_UserStickerPack> {
|
||||
return this.authedPost("/api/v1/dimension/stickers/packs/import", {packUrl}).map(r => r.json()).toPromise();
|
||||
}
|
||||
}
|
||||
|
1
web/public/licenses/general-imported.txt
Normal file
1
web/public/licenses/general-imported.txt
Normal file
@ -0,0 +1 @@
|
||||
The sticker pack was created by a Matrix user, however the license is unavailable. Sorry.
|
Loading…
Reference in New Issue
Block a user