From e76c78a59a84f574cdbcdb9db70655a25ac575ea Mon Sep 17 00:00:00 2001 From: Michael DiCarlo Date: Fri, 2 Apr 2021 07:13:43 -0400 Subject: [PATCH 1/8] Start v3.0.29 --- electron-app/package.json | 2 +- package.json | 2 +- ui/package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/electron-app/package.json b/electron-app/package.json index 6acd767b..b0127eda 100644 --- a/electron-app/package.json +++ b/electron-app/package.json @@ -1,6 +1,6 @@ { "name": "postybirb-plus", - "version": "3.0.28", + "version": "3.0.29", "description": "(ClientServer) PostyBirb is an application that helps artists post art and other multimedia to multiple websites more quickly.", "main": "dist/main.js", "author": "Michael DiCarlo", diff --git a/package.json b/package.json index 91c62597..d024898a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "postybirb-plus", - "version": "3.0.28", + "version": "3.0.29", "description": "PostyBirb is an application that helps artists post art and other multimedia to multiple websites more quickly..", "main": "index.js", "scripts": { diff --git a/ui/package.json b/ui/package.json index 5ecf6874..57815469 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,6 +1,6 @@ { "name": "postybirb-plus-ui", - "version": "3.0.28", + "version": "3.0.29", "license": "BSD-3-Clause", "private": true, "Author": "Michael DiCarlo", From 958ffcaff3f039f364f11aed1337d36a66d97480 Mon Sep 17 00:00:00 2001 From: Michael DiCarlo Date: Fri, 2 Apr 2021 07:40:57 -0400 Subject: [PATCH 2/8] Weasyl: Added 'None' option for folders --- .../patreon/patreon.file.options.interface.ts | 1 + .../src/websites/patreon/patreon.file.options.ts | 7 ++++++- .../server/websites/patreon/patreon.service.ts | 4 ++++ ui/src/websites/patreon/Patreon.tsx | 16 ++++++++++++---- ui/src/websites/weasyl/Weasyl.tsx | 1 + 5 files changed, 24 insertions(+), 5 deletions(-) diff --git a/commons/src/interfaces/websites/patreon/patreon.file.options.interface.ts b/commons/src/interfaces/websites/patreon/patreon.file.options.interface.ts index df6f244e..38f1dbb8 100644 --- a/commons/src/interfaces/websites/patreon/patreon.file.options.interface.ts +++ b/commons/src/interfaces/websites/patreon/patreon.file.options.interface.ts @@ -6,4 +6,5 @@ export interface PatreonFileOptions extends DefaultFileOptions { schedule?: string; // as date string teaser?: string; allAsAttachment: boolean; + earlyAccess?: Date; } diff --git a/commons/src/websites/patreon/patreon.file.options.ts b/commons/src/websites/patreon/patreon.file.options.ts index ddc4f97f..1c7d8111 100644 --- a/commons/src/websites/patreon/patreon.file.options.ts +++ b/commons/src/websites/patreon/patreon.file.options.ts @@ -1,5 +1,5 @@ import { Expose } from 'class-transformer'; -import { IsArray, IsBoolean, IsDateString, IsOptional, IsString } from 'class-validator'; +import { IsArray, IsBoolean, IsDate, IsDateString, IsOptional, IsString } from 'class-validator'; import { DefaultFileOptions } from '../../interfaces/submission/default-options.interface'; import { PatreonFileOptions } from '../../interfaces/websites/patreon/patreon.file.options.interface'; import { DefaultValue } from '../../models/decorators/default-value.decorator'; @@ -32,6 +32,11 @@ export class PatreonFileOptionsEntity extends DefaultFileOptionsEntity @DefaultValue(false) allAsAttachment!: boolean; + @Expose() + @IsOptional() + @IsDate() + earlyAccess?: Date; + constructor(entity?: Partial) { super(entity as DefaultFileOptions); } diff --git a/electron-app/src/server/websites/patreon/patreon.service.ts b/electron-app/src/server/websites/patreon/patreon.service.ts index 2da43be3..834f28a2 100644 --- a/electron-app/src/server/websites/patreon/patreon.service.ts +++ b/electron-app/src/server/websites/patreon/patreon.service.ts @@ -466,6 +466,10 @@ export class Patreon extends Website { attributes.tags.publish = false; } + if (options.earlyAccess) { + attributes.change_visibility_at = this.toUTCISO(options.earlyAccess); + } + const relationships = { post_tag: { data: relationshipTags.length > 0 ? relationshipTags[0] : {}, diff --git a/ui/src/websites/patreon/Patreon.tsx b/ui/src/websites/patreon/Patreon.tsx index 045d68eb..391ccb6a 100644 --- a/ui/src/websites/patreon/Patreon.tsx +++ b/ui/src/websites/patreon/Patreon.tsx @@ -282,10 +282,18 @@ export class PatreonFileSubmissionForm extends GenericFileSubmissionSection , - + + + this.setValue('earlyAccess', value ? value.toDate().toString() : undefined) + } + /> + , + + None {this.state.folders.map(f => ( {f.label} ))} From 97e1ecee0744dc0084f056e8c4efe69ca4e17f61 Mon Sep 17 00:00:00 2001 From: Michael DiCarlo Date: Fri, 2 Apr 2021 07:47:02 -0400 Subject: [PATCH 3/8] Newgrounds: Alter newgrounds error for accepting tos --- .../websites/newgrounds/newgrounds.service.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/electron-app/src/server/websites/newgrounds/newgrounds.service.ts b/electron-app/src/server/websites/newgrounds/newgrounds.service.ts index 132e78ff..45b17d87 100644 --- a/electron-app/src/server/websites/newgrounds/newgrounds.service.ts +++ b/electron-app/src/server/websites/newgrounds/newgrounds.service.ts @@ -203,7 +203,7 @@ export class Newgrounds extends Website { } const newCookies: any = {}; - parkFile.response.headers['set-cookie'].forEach(cookie => { + parkFile.response.headers['set-cookie'].forEach((cookie) => { const cookieParts = cookie.split(';')[0].split('='); return (newCookies[cookieParts[0]] = cookieParts[1]); }); @@ -240,7 +240,14 @@ export class Newgrounds extends Website { } else { let message = ''; try { - message = post.body.errors.join(' '); + message = post.body.errors + .map((err) => { + if (err.includes('You must agree to the terms of the submissions agreement.')) { + return 'You must first manually post to Newgrounds to accept the terms of the submissions agreement.'; + } + return err; + }) + .join(' '); } catch (err) { message = (cheerio.load(post.body.error) as any).text(); } @@ -256,7 +263,7 @@ export class Newgrounds extends Website { formatTags(tags: string[]): any { return super .formatTags(tags, { spaceReplacer: '-' }) - .map(tag => { + .map((tag) => { return tag.replace(/(\(|\)|:|#|;|\]|\[|')/g, '').replace(/_/g, '-'); }) .slice(0, 12); @@ -288,9 +295,7 @@ export class Newgrounds extends Website { } if (!WebsiteValidator.supportsFileType(submission.primary, this.acceptsFiles)) { - problems.push( - `Currently supported file formats: ${this.acceptsFiles.join(', ')}`, - ); + problems.push(`Currently supported file formats: ${this.acceptsFiles.join(', ')}`); } const { type, size, name } = submission.primary; From 6d8471011d45a0b7523bf703b866d6b8ae47ee2e Mon Sep 17 00:00:00 2001 From: Michael DiCarlo Date: Fri, 2 Apr 2021 07:55:25 -0400 Subject: [PATCH 4/8] Telegram: Links made more obvious on login page --- ui/src/websites/telegram/TelegramLogin.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/src/websites/telegram/TelegramLogin.tsx b/ui/src/websites/telegram/TelegramLogin.tsx index e7c72ea0..c67752d7 100644 --- a/ui/src/websites/telegram/TelegramLogin.tsx +++ b/ui/src/websites/telegram/TelegramLogin.tsx @@ -1,4 +1,4 @@ -import { Button, Form, Input, message, Modal } from 'antd'; +import { Button, Form, Icon, Input, message, Modal } from 'antd'; import { TelegramAccountData } from 'postybirb-commons'; import React from 'react'; import BrowserLink from '../../components/BrowserLink'; @@ -80,7 +80,7 @@ export default class TelegramLogin extends React.Component - You must create you own app configuration. + You must create you own app configuration } @@ -97,7 +97,7 @@ export default class TelegramLogin extends React.Component - You must create you own app configuration. + You must create you own app configuration } @@ -115,7 +115,7 @@ export default class TelegramLogin extends React.Component - Phone number must be in international format. + Phone number must be in international format } From 624e7fa106b6d51550c0742bc00349dd53584f18 Mon Sep 17 00:00:00 2001 From: Michael DiCarlo Date: Fri, 2 Apr 2021 08:10:43 -0400 Subject: [PATCH 5/8] Telegram: Handle text that is long differently for files if it wouldn't fit caption --- .../websites/telegram/telegram.service.ts | 52 +++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/electron-app/src/server/websites/telegram/telegram.service.ts b/electron-app/src/server/websites/telegram/telegram.service.ts index 7d44f624..701c37ae 100644 --- a/electron-app/src/server/websites/telegram/telegram.service.ts +++ b/electron-app/src/server/websites/telegram/telegram.service.ts @@ -30,6 +30,7 @@ import { ScalingOptions } from '../interfaces/scaling-options.interface'; import { Website } from '../website.base'; import { TelegramStorage } from './telegram.storage'; import _ = require('lodash'); +import FormContent from 'src/server/utils/form-content.util'; @Injectable() export class Telegram extends Website { @@ -269,6 +270,8 @@ export class Telegram extends Website { fileData.push(await this.upload(appId, file)); } + const description = data.description.slice(0, 4096).trim(); + for (const channel of data.options.channels) { this.checkCancelled(cancellationToken); const [channel_id, access_hash] = channel.split('-'); @@ -278,21 +281,48 @@ export class Telegram extends Website { access_hash, }; if (files.length === 1) { + let messagePosted = false; + if (description.length > 1024) { + messagePosted = true; + await this.callApi(accountData.appId, 'messages.sendMessage', { + random_id: Date.now(), + message: data.description.slice(0, 4096).trim(), + peer: { + _: 'inputPeerChannel', + channel_id, + access_hash, + }, + }); + } await this.callApi(appId, 'messages.sendMedia', { random_id: Date.now(), media: fileData[0], - message: data.description, + message: messagePosted ? '' : data.description, peer, silent: data.options.silent, }); } else { + let messagePosted = false; + if (description.length > 1024) { + messagePosted = true; + await this.callApi(accountData.appId, 'messages.sendMessage', { + random_id: Date.now(), + message: data.description.slice(0, 4096).trim(), + peer: { + _: 'inputPeerChannel', + channel_id, + access_hash, + }, + }); + } + // multimedia send const id = Date.now(); for (let i = 0; i < fileData.length; i++) { await this.callApi(appId, 'messages.sendMedia', { random_id: id + i, media: fileData[i], - message: i === 0 ? data.description : '', + message: messagePosted ? '' : i === 0 ? data.description : '', peer, silent: data.options.silent, }); @@ -316,7 +346,7 @@ export class Telegram extends Website { const [channel_id, access_hash] = channel.split('-'); await this.callApi(accountData.appId, 'messages.sendMessage', { random_id: Date.now(), - message: data.description, + message: data.description.slice(0, 4096).trim(), peer: { _: 'inputPeerChannel', channel_id, @@ -371,6 +401,14 @@ export class Telegram extends Website { } }); + const description = this.defaultDescriptionParser( + FormContent.getDescription(defaultPart.data.description, submissionPart.data.description), + ); + + if (description.length > 4096) { + warnings.push('Max description length allowed is 4,096 characters.'); + } + return { problems, warnings }; } @@ -397,6 +435,14 @@ export class Telegram extends Website { problems.push('No channel(s) selected.'); } + const description = this.defaultDescriptionParser( + FormContent.getDescription(defaultPart.data.description, submissionPart.data.description), + ); + + if (description.length > 4096) { + warnings.push('Max description length allowed is 4,096 characters.'); + } + return { problems, warnings }; } } From 447a1aa7c7e8026c9f3fb095393422286a0ef5bb Mon Sep 17 00:00:00 2001 From: Michael DiCarlo Date: Fri, 2 Apr 2021 08:25:34 -0400 Subject: [PATCH 6/8] Build: Cleaned up builds scripts and made them run in parallel --- electron-app/package.json | 3 ++- package.json | 18 ++++++++++++------ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/electron-app/package.json b/electron-app/package.json index b0127eda..6f322b87 100644 --- a/electron-app/package.json +++ b/electron-app/package.json @@ -16,11 +16,12 @@ "postinstall": "electron-builder install-app-deps", "build": "nest build", "build:linux": "electron-builder -l", - "build:mac": "electron-builder -m", + "build:osx": "electron-builder -m", "build:windows": "electron-builder -w", "build:release": "export $(cat .env | xargs) && electron-builder -mwl -p always", "release:windows": "electron-builder -w -p always", "release:linux": "electron-builder -l -p always", + "release:osx": "export $(cat .env | xargs) && electron-builder -m -p always", "clean": "rimraf release", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "start": "npm run prebuild && nest build && electron dist/main --develop", diff --git a/package.json b/package.json index d024898a..5fef2f11 100644 --- a/package.json +++ b/package.json @@ -4,12 +4,17 @@ "description": "PostyBirb is an application that helps artists post art and other multimedia to multiple websites more quickly..", "main": "index.js", "scripts": { - "build": "npm run build:common && yarn run build:ui && yarn run build:app", - "build:app": "cd electron-app && npm install && yarn run build", - "build:common": "cd commons && npm install && npm run build", - "build:ui": "cd ui && npm install && yarn run build", - "release:windows": "node create-signer.js && yarn run build && cd electron-app && yarn run release:windows", - "release:linux": "node create-signer.js && yarn run build && cd electron-app && yarn run release:linux" + "install": "run-p install:**", + "install:commons": "cd commons && npm install && npm run build", + "install:app": "cd electron-app && npm install", + "install:ui": "cd ui && npm install", + "build": "run-p build:**", + "build:app": "cd electron-app && npm run build", + "build:ui": "cd ui && npm run build", + "make": "run-s install build", + "release:windows": "node create-signer.js && npm run make && cd electron-app && yarn run release:windows", + "release:linux": "node create-signer.js && npm run make && cd electron-app && yarn run release:linux", + "release:osx": "npm run make && cd electron-app && yarn run release:osx" }, "repository": { "type": "git", @@ -23,6 +28,7 @@ "homepage": "https://github.com/mvdicarlo/postybirb-plus#readme", "dependencies": { "@mtproto/core": "^5.3.0", + "npm-run-all": "^4.1.5", "yarn": "^1.22.4" } } From 3576798f424b234e157aae902527efa43d4d42d9 Mon Sep 17 00:00:00 2001 From: Michael DiCarlo Date: Fri, 9 Apr 2021 07:56:44 -0400 Subject: [PATCH 7/8] Mastodon: Threading and ordering --- .../websites/mastodon/mastodon.service.ts | 57 ++++++++++++------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/electron-app/src/server/websites/mastodon/mastodon.service.ts b/electron-app/src/server/websites/mastodon/mastodon.service.ts index a6d07e9d..a0012726 100644 --- a/electron-app/src/server/websites/mastodon/mastodon.service.ts +++ b/electron-app/src/server/websites/mastodon/mastodon.service.ts @@ -30,6 +30,7 @@ import WebsiteValidator from 'src/server/utils/website-validator.util'; import { LoginResponse } from '../interfaces/login-response.interface'; import { ScalingOptions } from '../interfaces/scaling-options.interface'; import { Website } from '../website.base'; +import * as _ from 'lodash'; @Injectable() export class Mastodon extends Website { @@ -81,7 +82,7 @@ export class Mastodon extends Website { return { maxSize: FileSize.MBtoBytes(300) }; } - private async uploadMedia(data: MastodonAccountData, file: PostFile): Promise { + private async uploadMedia(data: MastodonAccountData, file: PostFile): Promise<{ id: string }> { const upload = await Http.post<{ id: string; errors: any }>( `${data.website}/api/v1/media`, undefined, @@ -106,7 +107,7 @@ export class Mastodon extends Website { ); } - return upload.body.id; + return { id: upload.body.id }; } async postFileSubmission( @@ -116,29 +117,47 @@ export class Mastodon extends Website { ): Promise { const M = this.getMastodonInstance(accountData); - const files = [data.primary, ...data.additional].slice(0, 4); + const files = [data.primary, ...data.additional]; this.checkCancelled(cancellationToken); - const uploadIds = await Promise.all( - files.map(file => this.uploadMedia(accountData, file.file)), - ); + const uploadedMedias: { + id: string; + }[] = []; + for (const file of files) { + uploadedMedias.push(await this.uploadMedia(accountData, file.file)); + } const isSensitive = data.rating !== SubmissionRating.GENERAL; - const { options } = data; - const form: any = { - status: `${options.useTitle && data.title ? `${data.title}\n` : ''}${ - data.description - }`.substring(0, 500), - sensitive: isSensitive, - media_ids: uploadIds, - }; + const chunks = _.chunk(uploadedMedias, 4); + let lastId = undefined; + for (let i = 0; i < chunks.length; i++) { + let form = undefined; + if (i === 0) { + form = { + status: `${options.useTitle && data.title ? `${data.title}\n` : ''}${ + data.description + }`.substring(0, 500), + sensitive: isSensitive, + media_ids: chunks[i].map((media) => media.id), + }; + } else { + form = { + sensitive: isSensitive, + media_ids: chunks[i].map((media) => media.id), + in_reply_to_id: lastId, + }; + } - if (options.spoilerText) { - form.spoiler_text = options.spoilerText; + if (options.spoilerText) { + form.spoiler_text = options.spoilerText; + } + + const post = await M.post('statuses', form); + lastId = post.data.id; } this.checkCancelled(cancellationToken); - const post = await M.post('statuses', form); + return this.createPostResponse({}); } @@ -188,12 +207,12 @@ export class Mastodon extends Website { const files = [ submission.primary, ...(submission.additional || []).filter( - f => !f.ignoredAccounts!.includes(submissionPart.accountId), + (f) => !f.ignoredAccounts!.includes(submissionPart.accountId), ), ]; const maxMB: number = 300; - files.forEach(file => { + files.forEach((file) => { const { type, size, name, mimetype } = file; if (!WebsiteValidator.supportsFileType(file, this.acceptsFiles)) { problems.push(`Does not support file format: (${name}) ${mimetype}.`); From 8c2045a7f98c20980a2276ab8e4096edd3036b64 Mon Sep 17 00:00:00 2001 From: Michael DiCarlo Date: Fri, 9 Apr 2021 08:32:12 -0400 Subject: [PATCH 8/8] Telegram: Better telegram response message on failure --- .../websites/telegram/telegram.service.ts | 6 ++++-- ui/src/websites/telegram/TelegramLogin.tsx | 20 +++++++++++-------- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/electron-app/src/server/websites/telegram/telegram.service.ts b/electron-app/src/server/websites/telegram/telegram.service.ts index 701c37ae..c6fa2d31 100644 --- a/electron-app/src/server/websites/telegram/telegram.service.ts +++ b/electron-app/src/server/websites/telegram/telegram.service.ts @@ -90,10 +90,12 @@ export class Telegram extends Website { phone_code: data.code, phone_code_hash: this.authData[data.appId].phone_code_hash, }) - .then(() => true) + .then(() => { + result: true; + }) .catch((err) => { this.logger.error(err); - return false; + return { result: false, message: err.error_message }; }); } diff --git a/ui/src/websites/telegram/TelegramLogin.tsx b/ui/src/websites/telegram/TelegramLogin.tsx index c67752d7..ecbf030c 100644 --- a/ui/src/websites/telegram/TelegramLogin.tsx +++ b/ui/src/websites/telegram/TelegramLogin.tsx @@ -97,7 +97,7 @@ export default class TelegramLogin extends React.Component - You must create you own app configuration + You must create your own app configuration } @@ -147,16 +147,20 @@ export default class TelegramLogin extends React.Component(this.props.account.website, 'authenticate', { - appId: this.state.appId, - code: this.state.code - }) - .then(success => { - if (success) { + WebsiteService.postCustomRoute<{ result: boolean; message?: string }>( + this.props.account.website, + 'authenticate', + { + appId: this.state.appId, + code: this.state.code + } + ) + .then(res => { + if (res.result) { message.success('Telegram authenticated.'); this.setState({ displayCodeDialog: false }); } else { - message.error('Failed to authenticate Telegram.'); + message.error(res.message || 'Failed to authenticate Telegram.'); } }) .catch(() => {