Skip to content

Commit

Permalink
Merge pull request #245 from mvdicarlo/develop
Browse files Browse the repository at this point in the history
v3.1.29
  • Loading branch information
mvdicarlo authored Sep 17, 2023
2 parents 215e1e9 + 73ff142 commit 0ac6f5f
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 296 deletions.
2 changes: 1 addition & 1 deletion electron-app/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down
176 changes: 49 additions & 127 deletions electron-app/src/server/websites/mastodon/mastodon.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<boolean> {
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
Expand All @@ -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<string> {
const upload = await Http.post<{ id: string; errors: any; url: string }>(
`${data.website}/api/v2/media`,
undefined,
Expand Down Expand Up @@ -180,7 +165,7 @@ export class Mastodon extends Website {
);
}

return { id: upload.body.id };
return upload.body.id;
}

async postFileSubmission(
Expand All @@ -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<Entity.Status>;
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<Entity.Status>;
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({
Expand All @@ -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(
Expand All @@ -293,49 +238,27 @@ export class Mastodon extends Website {
): Promise<PostResponse> {
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<Entity.Status>;
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));
}
}

Expand Down Expand Up @@ -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(
Expand All @@ -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;
Expand All @@ -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`,
);
}
});

Expand All @@ -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.`,
);
}

Expand All @@ -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).`,
Expand Down
Loading

0 comments on commit 0ac6f5f

Please sign in to comment.