From 0e15983decd9010307936bd83a2eef9f457ca844 Mon Sep 17 00:00:00 2001 From: Xenoyia Date: Sun, 17 Sep 2023 17:36:12 +0100 Subject: [PATCH 1/3] Furtastic: Fixed broken imports. (#244) Fixes Furtastic imports Co-authored-by: Lemonynade --- .../server/websites/website-provider.service.ts | 2 ++ .../src/server/websites/websites.module.ts | 2 ++ ui/src/websites/furtastic/Furtastic.tsx | 14 -------------- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/electron-app/src/server/websites/website-provider.service.ts b/electron-app/src/server/websites/website-provider.service.ts index d4659f62..ed755f77 100644 --- a/electron-app/src/server/websites/website-provider.service.ts +++ b/electron-app/src/server/websites/website-provider.service.ts @@ -17,6 +17,7 @@ import { Custom } from './custom/custom.service'; import { Newgrounds } from './newgrounds/newgrounds.service'; import { Pixiv } from './pixiv/pixiv.service'; import { FurryLife } from './furry-life/furry-life.service'; +import { Furtastic } from './furtastic/furtastic.service'; import { FurryNetwork } from './furry-network/furry-network.service'; import { Patreon } from './patreon/patreon.service'; import { Tumblr } from './tumblr/tumblr.service'; @@ -51,6 +52,7 @@ export class WebsiteProvider { readonly sofurry: SoFurry, readonly e621: e621, readonly furaffinity: FurAffinity, + readonly furtastic: Furtastic, readonly subscribestar: SubscribeStar, readonly subscribestarAdult: SubscribeStarAdult, readonly hentaiFoundry: HentaiFoundry, diff --git a/electron-app/src/server/websites/websites.module.ts b/electron-app/src/server/websites/websites.module.ts index 19bcf4c4..42406496 100644 --- a/electron-app/src/server/websites/websites.module.ts +++ b/electron-app/src/server/websites/websites.module.ts @@ -12,6 +12,7 @@ import { InkbunnyModule } from './inkbunny/inkbunny.module'; import { SoFurryModule } from './so-furry/so-furry.module'; import { E621Module } from './e621/e621.module'; import { FurAffinityModule } from './fur-affinity/fur-affinity.module'; +import { FurtasticModule } from './furtastic/furtastic.module'; import { SubscribeStarModule } from './subscribe-star/subscribe-star.module'; import { HentaiFoundryModule } from './hentai-foundry/hentai-foundry.module'; import { AryionModule } from './aryion/aryion.module'; @@ -50,6 +51,7 @@ import { BlueskyModule } from './bluesky/bluesky.module'; DiscordModule, E621Module, FurAffinityModule, + FurtasticModule, FurbooruModule, FurryLifeModule, FurryNetworkModule, diff --git a/ui/src/websites/furtastic/Furtastic.tsx b/ui/src/websites/furtastic/Furtastic.tsx index 463773c6..4a48f22d 100644 --- a/ui/src/websites/furtastic/Furtastic.tsx +++ b/ui/src/websites/furtastic/Furtastic.tsx @@ -38,22 +38,8 @@ export class FurtasticFileSubmissionForm extends GenericFileSubmissionSection - - - ); - } - return sources; - } - renderRightForm(data: FurtasticFileOptions) { const elements = super.renderRightForm(data); - elements.push(...this.getSourceSection()); return elements; } } From 45d18cdae04cdb3abd50b1f1c4acf092cc3a8559 Mon Sep 17 00:00:00 2001 From: Leaftail <110915645+leaftail1880@users.noreply.github.com> Date: Sun, 17 Sep 2023 19:40:33 +0300 Subject: [PATCH 2/3] fix: mastodon and misskey (#243) better support for source on this cites, less duplicated code and error fix for misskey Co-authored-by: leaftail1880 <110915645+leaftaul1880@users.noreply.github.com> --- .../websites/mastodon/mastodon.service.ts | 176 ++++----------- .../websites/misskey/misskey.service.ts | 213 +++++------------- .../src/server/websites/website.base.ts | 18 ++ 3 files changed, 128 insertions(+), 279 deletions(-) diff --git a/electron-app/src/server/websites/mastodon/mastodon.service.ts b/electron-app/src/server/websites/mastodon/mastodon.service.ts index 684dde21..55f52516 100644 --- a/electron-app/src/server/websites/mastodon/mastodon.service.ts +++ b/electron-app/src/server/websites/mastodon/mastodon.service.ts @@ -77,37 +77,21 @@ export class Mastodon extends Website { const status: LoginResponse = { loggedIn: false, username: null }; const accountData: MastodonAccountData = data.data; if (accountData && accountData.token) { - const refresh = await this.refreshToken(accountData); - if (refresh) { - status.loggedIn = true; - status.username = accountData.username; - this.getInstanceInfo(data._id, accountData); - } + await this.getAndStoreInstanceInfo(data._id, accountData); + + status.loggedIn = true; + status.username = accountData.username; } return status; } - private async getInstanceInfo(profileId: string, data: MastodonAccountData) { + private async getAndStoreInstanceInfo(profileId: string, data: MastodonAccountData) { const client = generator('mastodon', data.website, data.token); const instance = await client.getInstance(); this.storeAccountInformation(profileId, INFO_KEY, instance.data); } - // TBH not entirely sure why this is a thing, but its in the old code so... :shrug: - private async refreshToken(data: MastodonAccountData): Promise { - const M = this.getMastodonInstance(data); - return true; - } - - private getMastodonInstance(data: MastodonAccountData): Entity.Instance { - const client = generator('mastodon', data.website, data.token); - client.getInstance().then(res => { - return res.data; - }); - return null; - } - getScalingOptions(file: FileRecord, accountId: string): ScalingOptions { const instanceInfo: MastodonInstanceInfo = this.getAccountInfo(accountId, INFO_KEY); return instanceInfo?.configuration?.media_attachments @@ -126,11 +110,12 @@ export class Mastodon extends Website { }; } + // Megaladon api has uploadMedia method, hovewer, it does not work with mastodon private async uploadMedia( data: MastodonAccountData, file: PostFile, altText: string, - ): Promise<{ id: string }> { + ): Promise { const upload = await Http.post<{ id: string; errors: any; url: string }>( `${data.website}/api/v2/media`, undefined, @@ -180,7 +165,7 @@ export class Mastodon extends Website { ); } - return { id: upload.body.id }; + return upload.body.id; } async postFileSubmission( @@ -191,85 +176,45 @@ export class Mastodon extends Website { const M = generator('mastodon', accountData.website, accountData.token); const files = [data.primary, ...data.additional]; - this.checkCancelled(cancellationToken); - const uploadedMedias: { - id: string; - }[] = []; + const uploadedMedias: string[] = []; for (const file of files) { + this.checkCancelled(cancellationToken); uploadedMedias.push(await this.uploadMedia(accountData, file.file, data.options.altText)); } const instanceInfo: MastodonInstanceInfo = this.getAccountInfo(data.part.accountId, INFO_KEY); - const chunkCount = instanceInfo - ? instanceInfo?.configuration?.statuses?.max_media_attachments - : 4; - const maxChars = instanceInfo ? instanceInfo?.configuration?.statuses?.max_characters : 500; + const chunkCount = instanceInfo?.configuration?.statuses?.max_media_attachments ?? 4; + const maxChars = instanceInfo?.configuration?.statuses?.max_characters ?? 500; const isSensitive = data.rating !== SubmissionRating.GENERAL; - const { options } = data; const chunks = _.chunk(uploadedMedias, chunkCount); - let lastId = undefined; - let status = ''; - let result: Response; + let status = `${data.options.useTitle && data.title ? `${data.title}\n` : ''}${ + data.description + }`.substring(0, maxChars); + let lastId = ''; + let source = ''; for (let i = 0; i < chunks.length; i++) { this.checkCancelled(cancellationToken); - - let statusOptions: any = { - status: '', + const statusOptions: any = { sensitive: isSensitive, - visibility: options.visibility || 'public', - spoiler_text: '', + visibility: data.options.visibility || 'public', + media_ids: chunks[i], }; - if (i === 0) { - status = `${options.useTitle && data.title ? `${data.title}\n` : ''}${ - data.description - }`.substring(0, maxChars); - - statusOptions = { - sensitive: isSensitive, - visibility: options.visibility || 'public', - media_ids: chunks[i].map(media => media.id), - spoiler_text: '', - }; - } else { - statusOptions = { - sensitive: isSensitive, - visibility: options.visibility || 'public', - media_ids: chunks[i].map(media => media.id), - in_reply_to_id: lastId, - spoiler_text: '', - }; + if (i !== 0) { + statusOptions.in_reply_to_id = lastId; } - - const tags = this.formatTags(data.tags); - - // Update the post content with the Tags if any are specified - for Mastodon, we need to append - // these onto the post, *IF* there is character count available. - if (tags.length > 0) { - status += '\n\n'; + if (data.options.spoilerText) { + statusOptions.spoiler_text = data.options.spoilerText; } - tags.forEach(tag => { - let remain = maxChars - status.length; - let tagToInsert = tag; - if (!tag.startsWith('#')) { - tagToInsert = `#${tagToInsert}`; - } - if (remain > tagToInsert.length) { - status += ` ${tagToInsert}`; - } - // We don't exit the loop, so we can cram in every possible tag, even if there are short ones! - }); - - if (options.spoilerText) { - statusOptions.spoiler_text = options.spoilerText; - } + status = this.appendTags(this.formatTags(data.tags), status, maxChars); try { - result = (await M.postStatus(status, statusOptions)) as Response; - lastId = result.data.id; + const result = (await M.postStatus(status, statusOptions)).data as Entity.Status; + if (!source) source = result.url; + lastId = result.id; } catch (err) { return Promise.reject( this.createPostResponse({ @@ -283,7 +228,7 @@ export class Mastodon extends Website { this.checkCancelled(cancellationToken); - return this.createPostResponse({ source: result.data.url }); + return this.createPostResponse({ source }); } async postNotificationSubmission( @@ -293,49 +238,27 @@ export class Mastodon extends Website { ): Promise { const M = generator('mastodon', accountData.website, accountData.token); const instanceInfo: MastodonInstanceInfo = this.getAccountInfo(data.part.accountId, INFO_KEY); - const maxChars = instanceInfo ? instanceInfo?.configuration?.statuses?.max_characters : 500; + const maxChars = instanceInfo?.configuration?.statuses?.max_characters ?? 500; const isSensitive = data.rating !== SubmissionRating.GENERAL; - - const { options } = data; - let status = `${options.useTitle && data.title ? `${data.title}\n` : ''}${data.description}`; const statusOptions: any = { sensitive: isSensitive, - visibility: options.visibility || 'public', - spoiler_text: '', + visibility: data.options.visibility || 'public', }; - - const tags = this.formatTags(data.tags); - - // Update the post content with the Tags if any are specified - for Mastodon, we need to append - // these onto the post, *IF* there is character count available. - if (tags.length > 0) { - status += '\n\n'; - } - - tags.forEach(tag => { - let remain = maxChars - status.length; - let tagToInsert = tag; - if (!tag.startsWith('#')) { - tagToInsert = `#${tagToInsert}`; - } - if (remain > tagToInsert.length) { - status += ` ${tagToInsert}`; - } - // We don't exit the loop, so we can cram in every possible tag, even if there are short ones! - }); - - if (options.spoilerText) { - statusOptions.spoiler_text = options.spoilerText; + let status = `${data.options.useTitle && data.title ? `${data.title}\n` : ''}${ + data.description + }`; + if (data.options.spoilerText) { + statusOptions.spoiler_text = data.options.spoilerText; } + status = this.appendTags(this.formatTags(data.tags), status, maxChars); this.checkCancelled(cancellationToken); - try { - const result = (await M.postStatus(status, statusOptions)) as Response; - return this.createPostResponse({ source: result.data.url }); - } catch (err) { - return Promise.reject(this.createPostResponse({ message: err.message, stack: err.stack })); + const result = (await M.postStatus(status, statusOptions)).data as Entity.Status; + return this.createPostResponse({ source: result.url }); + } catch (error) { + return Promise.reject(this.createPostResponse(error)); } } @@ -370,7 +293,7 @@ export class Mastodon extends Website { submissionPart.accountId, INFO_KEY, ); - const maxChars = instanceInfo ? instanceInfo?.configuration?.statuses?.max_characters : 500; + const maxChars = instanceInfo?.configuration?.statuses?.max_characters ?? 500; if (description.length > maxChars) { warnings.push( @@ -385,9 +308,8 @@ export class Mastodon extends Website { ), ]; - const maxImageSize = instanceInfo - ? instanceInfo?.configuration?.media_attachments?.image_size_limit - : FileSize.MBtoBytes(50); + const maxImageSize = + instanceInfo?.configuration?.media_attachments?.image_size_limit ?? FileSize.MBtoBytes(50); files.forEach(file => { const { type, size, name, mimetype } = file; @@ -413,8 +335,9 @@ export class Mastodon extends Website { type === FileSubmissionType.IMAGE && (file.height > 4000 || file.width > 4000) ) { - warnings.push(`${name} will be scaled down to a maximum size of 4000x4000, while maintaining - aspect ratio`); + warnings.push( + `${name} will be scaled down to a maximum size of 4000x4000, while maintaining aspect ratio`, + ); } }); @@ -423,8 +346,7 @@ export class Mastodon extends Website { submissionPart.data.visibility != 'public' ) { warnings.push( - `This post won't be listed under any hashtag as it is not public. Only public posts - can be searched by hashtag.`, + `This post won't be listed under any hashtag as it is not public. Only public posts can be searched by hashtag.`, ); } @@ -445,7 +367,7 @@ export class Mastodon extends Website { submissionPart.accountId, INFO_KEY, ); - const maxChars = instanceInfo ? instanceInfo?.configuration?.statuses?.max_characters : 500; + const maxChars = instanceInfo?.configuration?.statuses?.max_characters ?? 500; if (description.length > maxChars) { warnings.push( `Max description length allowed is ${maxChars} characters (for this Mastodon client).`, diff --git a/electron-app/src/server/websites/misskey/misskey.service.ts b/electron-app/src/server/websites/misskey/misskey.service.ts index b3e67703..848d47fa 100644 --- a/electron-app/src/server/websites/misskey/misskey.service.ts +++ b/electron-app/src/server/websites/misskey/misskey.service.ts @@ -17,12 +17,8 @@ import { ScalingOptions } from '../interfaces/scaling-options.interface'; import UserAccountEntity from 'src/server//account/models/user-account.entity'; import { PlaintextParser } from 'src/server/description-parsing/plaintext/plaintext.parser'; import ImageManipulator from 'src/server/file-manipulation/manipulators/image.manipulator'; -import Http from 'src/server/http/http.util'; import { CancellationToken } from 'src/server/submission/post/cancellation/cancellation-token'; -import { - FilePostData, - PostFile, -} from 'src/server/submission/post/interfaces/file-post-data.interface'; +import { FilePostData } from 'src/server/submission/post/interfaces/file-post-data.interface'; import { PostData } from 'src/server/submission/post/interfaces/post-data.interface'; import { ValidationParts } from 'src/server/submission/validator/interfaces/validation-parts.interface'; import FileSize from 'src/server/utils/filesize.util'; @@ -31,7 +27,6 @@ import WebsiteValidator from 'src/server/utils/website-validator.util'; import { LoginResponse } from '../interfaces/login-response.interface'; import { Website } from '../website.base'; import _ from 'lodash'; -import WaitUtil from 'src/server/utils/wait.util'; import { FileManagerService } from 'src/server/file-manager/file-manager.service'; const INFO_KEY = 'INSTANCE INFO'; @@ -77,37 +72,21 @@ export class MissKey extends Website { const status: LoginResponse = { loggedIn: false, username: null }; const accountData: MissKeyAccountData = data.data; if (accountData && accountData.tokenData) { - const refresh = await this.refreshToken(accountData); - if (refresh) { - status.loggedIn = true; - status.username = accountData.username; - this.getInstanceInfo(data._id, accountData); - } + await this.getAndStoreInstanceInfo(data._id, accountData); + + status.loggedIn = true; + status.username = accountData.username; } return status; } - private async getInstanceInfo(profileId: string, data: MissKeyAccountData) { + private async getAndStoreInstanceInfo(profileId: string, data: MissKeyAccountData) { const client = generator('misskey', `https://${data.website}`, data.tokenData.access_token); const instance = await client.getInstance(); this.storeAccountInformation(profileId, INFO_KEY, instance.data); } - // TBH not entirely sure why this is a thing, but its in the old code so... :shrug: - private async refreshToken(data: MissKeyAccountData): Promise { - const M = this.getMissKeyInstance(data); - return true; - } - - private getMissKeyInstance(data: MissKeyAccountData): Entity.Instance { - const client = generator('misskey', `https://${data.website}`, data.tokenData.access_token); - client.getInstance().then(res => { - return res.data; - }); - return null; - } - getScalingOptions(file: FileRecord, accountId: string): ScalingOptions { const instanceInfo: MissKeyInstanceInfo = this.getAccountInfo(accountId, INFO_KEY); return instanceInfo?.configuration?.media_attachments @@ -126,23 +105,6 @@ export class MissKey extends Website { }; } - private async uploadMedia( - data: MissKeyAccountData, - file: PostFile, - altText: string, - ): Promise<{ id: string }> { - const M = generator('misskey', `https://${data.website}`, data.tokenData.access_token); - const upload = await M.uploadMedia(file.value, { description: altText }); - console.log(upload); - if (upload.status > 300) { - return Promise.reject( - this.createPostResponse({ additionalInfo: upload.status, message: upload.statusText }), - ); - } - - return { id: upload.data.id }; - } - async postFileSubmission( cancellationToken: CancellationToken, data: FilePostData, @@ -155,91 +117,59 @@ export class MissKey extends Website { ); const files = [data.primary, ...data.additional]; - this.checkCancelled(cancellationToken); - const uploadedMedias: { - id: string; - }[] = []; + const uploadedMedias: string[] = []; for (const file of files) { - uploadedMedias.push(await this.uploadMedia(accountData, file.file, data.options.altText)); + this.checkCancelled(cancellationToken); + const upload = await M.uploadMedia(file.file.value, { description: data.options.altText }); + if (upload.status > 300) { + return Promise.reject( + this.createPostResponse({ additionalInfo: upload.status, message: upload.statusText }), + ); + } + uploadedMedias.push(upload.data.id); } const instanceInfo: MissKeyInstanceInfo = this.getAccountInfo(data.part.accountId, INFO_KEY); - const chunkCount = instanceInfo - ? instanceInfo?.configuration?.statuses?.max_media_attachments - : 4; - const maxChars = instanceInfo ? instanceInfo?.configuration?.statuses?.max_characters : 500; + const chunkCount = instanceInfo?.configuration?.statuses?.max_media_attachments ?? 4; + const maxChars = instanceInfo?.configuration?.statuses?.max_characters ?? 500; const isSensitive = data.rating !== SubmissionRating.GENERAL; - const { options } = data; const chunks = _.chunk(uploadedMedias, chunkCount); - let lastId = undefined; + let status = `${data.options.useTitle && data.title ? `${data.title}\n` : ''}${ + data.description + }`.substring(0, maxChars); + let lastId = ''; + let source = ''; for (let i = 0; i < chunks.length; i++) { - let statusOptions: any = { - status: '', + const statusOptions: any = { sensitive: isSensitive, - visibility: options.visibility || 'public', - spoiler_text: '', + visibility: data.options.visibility || 'public', + media_ids: chunks[i], }; - let status = undefined; - - let form = undefined; - if (i === 0) { - status = `${options.useTitle && data.title ? `${data.title}\n` : ''}${ - data.description - }`.substring(0, maxChars); - - statusOptions = { - sensitive: isSensitive, - visibility: options.visibility || 'public', - media_ids: chunks[i].map(media => media.id), - spoiler_text: '', - }; - } else { - statusOptions = { - sensitive: isSensitive, - visibility: options.visibility || 'public', - media_ids: chunks[i].map(media => media.id), - in_reply_to_id: lastId, - spoiler_text: '', - }; - } - const tags = this.formatTags(data.tags); - - // Update the post content with the Tags if any are specified - for MissKey, we need to append - // these onto the post, *IF* there is character count available. - if (tags.length > 0) { - status += '\n\n'; + if (i !== 0) { + statusOptions.in_reply_to_id = lastId; } - - tags.forEach(tag => { - let remain = maxChars - form.status.length; - let tagToInsert = tag; - if (remain > tagToInsert.length) { - status += ` ${tagToInsert}`; - } - // We don't exit the loop, so we can cram in every possible tag, even if there are short ones! - }); - - if (options.spoilerText) { - statusOptions.spoiler_text = options.spoilerText; + if (data.options.spoilerText) { + statusOptions.spoiler_text = data.options.spoilerText; } - this.checkCancelled(cancellationToken); + status = this.appendTags(this.formatTags(data.tags), status, maxChars); - await M.postStatus(status, statusOptions) - .then(result => { - lastId = result.data.id; - let res = result.data as Entity.Status; - return this.createPostResponse({ source: res.url }); - }) - .catch((err: Error) => { - return Promise.reject(this.createPostResponse({ message: err.message })); - }); + this.checkCancelled(cancellationToken); + try { + const result = (await M.postStatus(status, statusOptions)) as Response; + lastId = result.data.id; + if (!source) source = (await M.getStatus(result.data.id)).data.url; + } catch (error) { + return Promise.reject(this.createPostResponse({ message: error.message })); + } } - return this.createPostResponse({}); + this.checkCancelled(cancellationToken); + + return this.createPostResponse({ source }); } async postNotificationSubmission( @@ -247,57 +177,36 @@ export class MissKey extends Website { data: PostData, accountData: MissKeyAccountData, ): Promise { - const mInstance = this.getMissKeyInstance(accountData); const M = generator( 'misskey', `https://${accountData.website}`, accountData.tokenData.access_token, ); - const maxChars = mInstance ? mInstance?.configuration?.statuses?.max_characters : 500; + const instanceInfo: MissKeyInstanceInfo = this.getAccountInfo(data.part.accountId, INFO_KEY); + const maxChars = instanceInfo?.configuration?.statuses?.max_characters ?? 500; const isSensitive = data.rating !== SubmissionRating.GENERAL; - - const { options } = data; - let status = `${options.useTitle && data.title ? `${data.title}\n` : ''}${data.description}`; const statusOptions: any = { sensitive: isSensitive, - visibility: options.visibility || 'public', - spoiler_text: '', + visibility: data.options.visibility || 'public', }; - - const tags = this.formatTags(data.tags); - - // Update the post content with the Tags if any are specified - for MissKey, we need to append - // these onto the post, *IF* there is character count available. - if (tags.length > 0) { - status += '\n\n'; - } - - tags.forEach(tag => { - let remain = maxChars - status.length; - let tagToInsert = tag; - if (remain > tagToInsert.length) { - status += ` ${tagToInsert}`; - } - // We don't exit the loop, so we can cram in every possible tag, even if there are short ones! - }); - - if (options.spoilerText) { - statusOptions.spoiler_text = options.spoilerText; + let status = `${data.options.useTitle && data.title ? `${data.title}\n` : ''}${ + data.description + }`; + if (data.options.spoilerText) { + statusOptions.spoiler_text = data.options.spoilerText; } + status = this.appendTags(this.formatTags(data.tags), status, maxChars); this.checkCancelled(cancellationToken); - - await M.postStatus(status, statusOptions) - .then(result => { - let res = result.data as Entity.Status; - return this.createPostResponse({ source: res.url }); - }) - .catch((err: Error) => { - return Promise.reject(this.createPostResponse({ message: err.message })); - }); - return this.createPostResponse({}); + try { + const result = (await M.postStatus(status, statusOptions)).data as Entity.Status; + const source = (await M.getStatus(result.id)).data.url; + return this.createPostResponse({ source }); + } catch (error) { + return Promise.reject(this.createPostResponse({ message: error.message })); + } } formatTags(tags: string[]) { @@ -374,8 +283,9 @@ export class MissKey extends Website { type === FileSubmissionType.IMAGE && (file.height > 4000 || file.width > 4000) ) { - warnings.push(`${name} will be scaled down to a maximum size of 4000x4000, while maintaining - aspect ratio`); + warnings.push( + `${name} will be scaled down to a maximum size of 4000x4000, while maintaining aspect ratio`, + ); } }); @@ -384,8 +294,7 @@ export class MissKey extends Website { submissionPart.data.visibility != 'public' ) { warnings.push( - `This post won't be listed under any hashtag as it is not public. Only public posts - can be searched by hashtag.`, + `This post won't be listed under any hashtag as it is not public. Only public posts can be searched by hashtag.`, ); } diff --git a/electron-app/src/server/websites/website.base.ts b/electron-app/src/server/websites/website.base.ts index 660b3038..86cca678 100644 --- a/electron-app/src/server/websites/website.base.ts +++ b/electron-app/src/server/websites/website.base.ts @@ -130,6 +130,24 @@ export abstract class Website { return this.parseTags(tags, options); } + /** + * Appends the tags to the description if there is enough character count available. + */ + appendTags(tags: string[], description: string, limit: number) { + if (!tags.length || description.length + 4 > limit) return description; + + description += '\n\n'; + + tags.forEach(tag => { + if (description.length + 1 + tag.length < limit) { + description += ` ${tag}`; + } + // We don't exit the loop, so we can cram in every possible tag, even if there are short ones! + }); + + return description; + } + parseDescription(text: string, type?: SubmissionType): string { return this.defaultDescriptionParser(text); } From 73ff142ffa94451e877f76f006080e8ae2062a3b Mon Sep 17 00:00:00 2001 From: Michael DiCarlo Date: Sun, 17 Sep 2023 12:41:50 -0400 Subject: [PATCH 3/3] v3.1.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 8b20bba0..7cfb2e55 100644 --- a/electron-app/package.json +++ b/electron-app/package.json @@ -1,6 +1,6 @@ { "name": "postybirb-plus", - "version": "3.1.28", + "version": "3.1.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 081cce78..67fa5ce5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "postybirb-plus", - "version": "3.1.28", + "version": "3.1.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 23d41e04..01c9c213 100644 --- a/ui/package.json +++ b/ui/package.json @@ -1,6 +1,6 @@ { "name": "postybirb-plus-ui", - "version": "3.1.28", + "version": "3.1.29", "license": "BSD-3-Clause", "private": true, "Author": "Michael DiCarlo",