feat: thread finisher for x and threads
This commit is contained in:
parent
8ec255b454
commit
762c53f640
|
|
@ -18,12 +18,14 @@ export const Editor = forwardRef<
|
|||
MDEditorProps & {
|
||||
order: number;
|
||||
totalPosts: number;
|
||||
disabledCopilot?: boolean;
|
||||
}
|
||||
>(
|
||||
(
|
||||
props: MDEditorProps & {
|
||||
order: number;
|
||||
totalPosts: number;
|
||||
disabledCopilot?: boolean;
|
||||
},
|
||||
ref: React.ForwardedRef<RefMDEditor>
|
||||
) => {
|
||||
|
|
@ -34,6 +36,7 @@ export const Editor = forwardRef<
|
|||
const t = useT();
|
||||
|
||||
useCopilotReadable({
|
||||
...(props.disabledCopilot ? { available: 'disabled' } : {}),
|
||||
description: 'Content of the post number ' + (props.order + 1),
|
||||
value: JSON.stringify({
|
||||
content: props.value,
|
||||
|
|
@ -42,6 +45,7 @@ export const Editor = forwardRef<
|
|||
}),
|
||||
});
|
||||
useCopilotAction({
|
||||
...(props.disabledCopilot ? { available: 'disabled' } : {}),
|
||||
name: 'editPost_' + props.order,
|
||||
description: `Edit the content of post number ${props.order}`,
|
||||
parameters: [
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
import { Slider } from '@gitroom/react/form/slider';
|
||||
import clsx from 'clsx';
|
||||
import { useState } from 'react';
|
||||
import { Editor } from '@gitroom/frontend/components/launches/editor';
|
||||
import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration';
|
||||
import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values';
|
||||
|
||||
export const ThreadFinisher = () => {
|
||||
const integration = useIntegration();
|
||||
const { register, watch, setValue } = useSettings();
|
||||
register('active_thread_finisher', {
|
||||
value: false,
|
||||
});
|
||||
|
||||
register('thread_finisher', {
|
||||
value: `That's a wrap!
|
||||
|
||||
If you enjoyed this thread:
|
||||
|
||||
1. Follow me @${integration.integration?.display || integration.integration?.name} for more of these
|
||||
2. RT the tweet below to share this thread with your audience`,
|
||||
});
|
||||
|
||||
const slider = watch('active_thread_finisher');
|
||||
const value = watch('thread_finisher');
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-[10px] border-tableBorder border p-[15px] rounded-lg mb-5">
|
||||
<div className="flex items-center">
|
||||
<div className="flex-1">Add a thread finisher</div>
|
||||
<div>
|
||||
<Slider
|
||||
value={slider ? 'on' : 'off'}
|
||||
onChange={(p) => setValue('active_thread_finisher', p === 'on')}
|
||||
fill={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full mt-[40px]">
|
||||
<div
|
||||
className={clsx(
|
||||
!slider && 'relative opacity-25 pointer-events-none editor'
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
<div className="flex gap-[4px]">
|
||||
<div className="flex-1 editor text-textColor">
|
||||
<Editor
|
||||
onChange={(val) => setValue('thread_finisher', val)}
|
||||
value={value}
|
||||
height={150}
|
||||
totalPosts={1}
|
||||
order={1}
|
||||
preview="edit"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -1,6 +1,11 @@
|
|||
import { withProvider } from '@gitroom/frontend/components/launches/providers/high.order.provider';
|
||||
import { ThreadFinisher } from '@gitroom/frontend/components/launches/finisher/thread.finisher';
|
||||
const SettingsComponent = () => {
|
||||
return <ThreadFinisher />;
|
||||
};
|
||||
|
||||
export default withProvider(
|
||||
null,
|
||||
SettingsComponent,
|
||||
undefined,
|
||||
undefined,
|
||||
async () => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
import { withProvider } from '@gitroom/frontend/components/launches/providers/high.order.provider';
|
||||
import { ThreadFinisher } from '@gitroom/frontend/components/launches/finisher/thread.finisher';
|
||||
|
||||
const SettingsComponent = () => {
|
||||
return <ThreadFinisher />;
|
||||
};
|
||||
|
||||
export default withProvider(
|
||||
null,
|
||||
SettingsComponent,
|
||||
undefined,
|
||||
undefined,
|
||||
async (posts) => {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
|
|||
const {
|
||||
id,
|
||||
name,
|
||||
username,
|
||||
picture: {
|
||||
data: { url },
|
||||
},
|
||||
|
|
@ -104,6 +105,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
|
|||
const {
|
||||
id,
|
||||
name,
|
||||
username,
|
||||
picture: {
|
||||
data: { url },
|
||||
},
|
||||
|
|
@ -116,7 +118,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
|
|||
refreshToken: access_token,
|
||||
expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(),
|
||||
picture: url,
|
||||
username: '',
|
||||
username: username,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -179,8 +181,6 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
|
|||
access_token: accessToken,
|
||||
});
|
||||
|
||||
console.log(mediaParams);
|
||||
|
||||
const { id: mediaId } = await (
|
||||
await this.fetch(
|
||||
`https://graph.threads.net/v1.0/${userId}/threads?${mediaParams.toString()}`,
|
||||
|
|
@ -243,7 +243,8 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
|
|||
userId: string,
|
||||
accessToken: string,
|
||||
message: string,
|
||||
replyToId?: string
|
||||
replyToId?: string,
|
||||
quoteId?: string
|
||||
): Promise<string> {
|
||||
const form = new FormData();
|
||||
form.append('media_type', 'TEXT');
|
||||
|
|
@ -254,6 +255,10 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
|
|||
form.append('reply_to_id', replyToId);
|
||||
}
|
||||
|
||||
if (quoteId) {
|
||||
form.append('quote_post_id', quoteId);
|
||||
}
|
||||
|
||||
const { id: contentId } = await (
|
||||
await this.fetch(`https://graph.threads.net/v1.0/${userId}/threads`, {
|
||||
method: 'POST',
|
||||
|
|
@ -293,7 +298,8 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
|
|||
userId: string,
|
||||
accessToken: string,
|
||||
postDetails: PostDetails,
|
||||
replyToId?: string
|
||||
replyToId?: string,
|
||||
quoteId?: string
|
||||
): Promise<string> {
|
||||
// Handle content creation based on media type
|
||||
if (!postDetails.media || postDetails.media.length === 0) {
|
||||
|
|
@ -302,7 +308,8 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
|
|||
userId,
|
||||
accessToken,
|
||||
postDetails.message,
|
||||
replyToId
|
||||
replyToId,
|
||||
quoteId
|
||||
);
|
||||
} else if (postDetails.media.length === 1) {
|
||||
// Single media content
|
||||
|
|
@ -329,7 +336,10 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
|
|||
async post(
|
||||
userId: string,
|
||||
accessToken: string,
|
||||
postDetails: PostDetails[]
|
||||
postDetails: PostDetails<{
|
||||
active_thread_finisher: boolean;
|
||||
thread_finisher: string;
|
||||
}>[]
|
||||
): Promise<PostResponse[]> {
|
||||
if (!postDetails.length) {
|
||||
return [];
|
||||
|
|
@ -392,6 +402,30 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
|
|||
});
|
||||
}
|
||||
|
||||
if (postDetails?.[0]?.settings?.active_thread_finisher) {
|
||||
try {
|
||||
const replyContentId = await this.createThreadContent(
|
||||
userId,
|
||||
accessToken,
|
||||
{
|
||||
id: makeId(10),
|
||||
media: [],
|
||||
message:
|
||||
postDetails?.[0]?.settings?.thread_finisher! +
|
||||
'\n' +
|
||||
responses[0].releaseURL,
|
||||
settings: {},
|
||||
},
|
||||
lastReplyId,
|
||||
threadId
|
||||
);
|
||||
|
||||
await this.publishThread(userId, accessToken, replyContentId);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
return responses;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,13 +9,11 @@ import {
|
|||
import { lookup } from 'mime-types';
|
||||
import sharp from 'sharp';
|
||||
import { readOrFetch } from '@gitroom/helpers/utils/read.or.fetch';
|
||||
import removeMd from 'remove-markdown';
|
||||
import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract';
|
||||
import { Plug } from '@gitroom/helpers/decorators/plug.decorator';
|
||||
import { Integration } from '@prisma/client';
|
||||
import { timer } from '@gitroom/helpers/utils/timer';
|
||||
import { PostPlug } from '@gitroom/helpers/decorators/post.plug';
|
||||
import { number, string } from 'yup';
|
||||
import dayjs from 'dayjs';
|
||||
import { uniqBy } from 'lodash';
|
||||
|
||||
|
|
@ -240,7 +238,10 @@ export class XProvider extends SocialAbstract implements SocialProvider {
|
|||
async post(
|
||||
id: string,
|
||||
accessToken: string,
|
||||
postDetails: PostDetails[]
|
||||
postDetails: PostDetails<{
|
||||
active_thread_finisher: boolean;
|
||||
thread_finisher: string;
|
||||
}>[]
|
||||
): Promise<PostResponse[]> {
|
||||
const [accessTokenSplit, accessSecretSplit] = accessToken.split(':');
|
||||
const client = new TwitterApi({
|
||||
|
|
@ -312,6 +313,18 @@ export class XProvider extends SocialAbstract implements SocialProvider {
|
|||
});
|
||||
}
|
||||
|
||||
if (postDetails?.[0]?.settings?.active_thread_finisher) {
|
||||
try {
|
||||
await client.v2.tweet({
|
||||
text:
|
||||
postDetails?.[0]?.settings?.thread_finisher! +
|
||||
'\n' +
|
||||
ids[0].releaseURL,
|
||||
reply: { in_reply_to_tweet_id: ids[ids.length - 1].postId },
|
||||
});
|
||||
} catch (err) {}
|
||||
}
|
||||
|
||||
return ids.map((p) => ({
|
||||
...p,
|
||||
status: 'posted',
|
||||
|
|
|
|||
Loading…
Reference in New Issue