Skip to content

Commit 762c53f

Browse files
committed
feat: thread finisher for x and threads
1 parent 8ec255b commit 762c53f

File tree

6 files changed

+137
-12
lines changed

6 files changed

+137
-12
lines changed

apps/frontend/src/components/launches/editor.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ export const Editor = forwardRef<
1818
MDEditorProps & {
1919
order: number;
2020
totalPosts: number;
21+
disabledCopilot?: boolean;
2122
}
2223
>(
2324
(
2425
props: MDEditorProps & {
2526
order: number;
2627
totalPosts: number;
28+
disabledCopilot?: boolean;
2729
},
2830
ref: React.ForwardedRef<RefMDEditor>
2931
) => {
@@ -34,6 +36,7 @@ export const Editor = forwardRef<
3436
const t = useT();
3537

3638
useCopilotReadable({
39+
...(props.disabledCopilot ? { available: 'disabled' } : {}),
3740
description: 'Content of the post number ' + (props.order + 1),
3841
value: JSON.stringify({
3942
content: props.value,
@@ -42,6 +45,7 @@ export const Editor = forwardRef<
4245
}),
4346
});
4447
useCopilotAction({
48+
...(props.disabledCopilot ? { available: 'disabled' } : {}),
4549
name: 'editPost_' + props.order,
4650
description: `Edit the content of post number ${props.order}`,
4751
parameters: [
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { Slider } from '@gitroom/react/form/slider';
2+
import clsx from 'clsx';
3+
import { useState } from 'react';
4+
import { Editor } from '@gitroom/frontend/components/launches/editor';
5+
import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration';
6+
import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values';
7+
8+
export const ThreadFinisher = () => {
9+
const integration = useIntegration();
10+
const { register, watch, setValue } = useSettings();
11+
register('active_thread_finisher', {
12+
value: false,
13+
});
14+
15+
register('thread_finisher', {
16+
value: `That's a wrap!
17+
18+
If you enjoyed this thread:
19+
20+
1. Follow me @${integration.integration?.display || integration.integration?.name} for more of these
21+
2. RT the tweet below to share this thread with your audience`,
22+
});
23+
24+
const slider = watch('active_thread_finisher');
25+
const value = watch('thread_finisher');
26+
27+
return (
28+
<div className="flex flex-col gap-[10px] border-tableBorder border p-[15px] rounded-lg mb-5">
29+
<div className="flex items-center">
30+
<div className="flex-1">Add a thread finisher</div>
31+
<div>
32+
<Slider
33+
value={slider ? 'on' : 'off'}
34+
onChange={(p) => setValue('active_thread_finisher', p === 'on')}
35+
fill={true}
36+
/>
37+
</div>
38+
</div>
39+
<div className="w-full mt-[40px]">
40+
<div
41+
className={clsx(
42+
!slider && 'relative opacity-25 pointer-events-none editor'
43+
)}
44+
>
45+
<div>
46+
<div className="flex gap-[4px]">
47+
<div className="flex-1 editor text-textColor">
48+
<Editor
49+
onChange={(val) => setValue('thread_finisher', val)}
50+
value={value}
51+
height={150}
52+
totalPosts={1}
53+
order={1}
54+
preview="edit"
55+
/>
56+
</div>
57+
</div>
58+
</div>
59+
</div>
60+
</div>
61+
</div>
62+
);
63+
};

apps/frontend/src/components/launches/providers/threads/threads.provider.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
import { withProvider } from '@gitroom/frontend/components/launches/providers/high.order.provider';
2+
import { ThreadFinisher } from '@gitroom/frontend/components/launches/finisher/thread.finisher';
3+
const SettingsComponent = () => {
4+
return <ThreadFinisher />;
5+
};
6+
27
export default withProvider(
3-
null,
8+
SettingsComponent,
49
undefined,
510
undefined,
611
async () => {

apps/frontend/src/components/launches/providers/x/x.provider.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { withProvider } from '@gitroom/frontend/components/launches/providers/high.order.provider';
2+
import { ThreadFinisher } from '@gitroom/frontend/components/launches/finisher/thread.finisher';
3+
4+
const SettingsComponent = () => {
5+
return <ThreadFinisher />;
6+
};
7+
28
export default withProvider(
3-
null,
9+
SettingsComponent,
410
undefined,
511
undefined,
612
async (posts) => {

libraries/nestjs-libraries/src/integrations/social/threads.provider.ts

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
3434
const {
3535
id,
3636
name,
37+
username,
3738
picture: {
3839
data: { url },
3940
},
@@ -104,6 +105,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
104105
const {
105106
id,
106107
name,
108+
username,
107109
picture: {
108110
data: { url },
109111
},
@@ -116,7 +118,7 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
116118
refreshToken: access_token,
117119
expiresIn: dayjs().add(59, 'days').unix() - dayjs().unix(),
118120
picture: url,
119-
username: '',
121+
username: username,
120122
};
121123
}
122124

@@ -179,8 +181,6 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
179181
access_token: accessToken,
180182
});
181183

182-
console.log(mediaParams);
183-
184184
const { id: mediaId } = await (
185185
await this.fetch(
186186
`https://graph.threads.net/v1.0/${userId}/threads?${mediaParams.toString()}`,
@@ -243,7 +243,8 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
243243
userId: string,
244244
accessToken: string,
245245
message: string,
246-
replyToId?: string
246+
replyToId?: string,
247+
quoteId?: string
247248
): Promise<string> {
248249
const form = new FormData();
249250
form.append('media_type', 'TEXT');
@@ -254,6 +255,10 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
254255
form.append('reply_to_id', replyToId);
255256
}
256257

258+
if (quoteId) {
259+
form.append('quote_post_id', quoteId);
260+
}
261+
257262
const { id: contentId } = await (
258263
await this.fetch(`https://graph.threads.net/v1.0/${userId}/threads`, {
259264
method: 'POST',
@@ -293,7 +298,8 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
293298
userId: string,
294299
accessToken: string,
295300
postDetails: PostDetails,
296-
replyToId?: string
301+
replyToId?: string,
302+
quoteId?: string
297303
): Promise<string> {
298304
// Handle content creation based on media type
299305
if (!postDetails.media || postDetails.media.length === 0) {
@@ -302,7 +308,8 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
302308
userId,
303309
accessToken,
304310
postDetails.message,
305-
replyToId
311+
replyToId,
312+
quoteId
306313
);
307314
} else if (postDetails.media.length === 1) {
308315
// Single media content
@@ -329,7 +336,10 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
329336
async post(
330337
userId: string,
331338
accessToken: string,
332-
postDetails: PostDetails[]
339+
postDetails: PostDetails<{
340+
active_thread_finisher: boolean;
341+
thread_finisher: string;
342+
}>[]
333343
): Promise<PostResponse[]> {
334344
if (!postDetails.length) {
335345
return [];
@@ -392,6 +402,30 @@ export class ThreadsProvider extends SocialAbstract implements SocialProvider {
392402
});
393403
}
394404

405+
if (postDetails?.[0]?.settings?.active_thread_finisher) {
406+
try {
407+
const replyContentId = await this.createThreadContent(
408+
userId,
409+
accessToken,
410+
{
411+
id: makeId(10),
412+
media: [],
413+
message:
414+
postDetails?.[0]?.settings?.thread_finisher! +
415+
'\n' +
416+
responses[0].releaseURL,
417+
settings: {},
418+
},
419+
lastReplyId,
420+
threadId
421+
);
422+
423+
await this.publishThread(userId, accessToken, replyContentId);
424+
} catch (err) {
425+
console.log(err);
426+
}
427+
}
428+
395429
return responses;
396430
}
397431

libraries/nestjs-libraries/src/integrations/social/x.provider.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,11 @@ import {
99
import { lookup } from 'mime-types';
1010
import sharp from 'sharp';
1111
import { readOrFetch } from '@gitroom/helpers/utils/read.or.fetch';
12-
import removeMd from 'remove-markdown';
1312
import { SocialAbstract } from '@gitroom/nestjs-libraries/integrations/social.abstract';
1413
import { Plug } from '@gitroom/helpers/decorators/plug.decorator';
1514
import { Integration } from '@prisma/client';
1615
import { timer } from '@gitroom/helpers/utils/timer';
1716
import { PostPlug } from '@gitroom/helpers/decorators/post.plug';
18-
import { number, string } from 'yup';
1917
import dayjs from 'dayjs';
2018
import { uniqBy } from 'lodash';
2119

@@ -240,7 +238,10 @@ export class XProvider extends SocialAbstract implements SocialProvider {
240238
async post(
241239
id: string,
242240
accessToken: string,
243-
postDetails: PostDetails[]
241+
postDetails: PostDetails<{
242+
active_thread_finisher: boolean;
243+
thread_finisher: string;
244+
}>[]
244245
): Promise<PostResponse[]> {
245246
const [accessTokenSplit, accessSecretSplit] = accessToken.split(':');
246247
const client = new TwitterApi({
@@ -312,6 +313,18 @@ export class XProvider extends SocialAbstract implements SocialProvider {
312313
});
313314
}
314315

316+
if (postDetails?.[0]?.settings?.active_thread_finisher) {
317+
try {
318+
await client.v2.tweet({
319+
text:
320+
postDetails?.[0]?.settings?.thread_finisher! +
321+
'\n' +
322+
ids[0].releaseURL,
323+
reply: { in_reply_to_tweet_id: ids[ids.length - 1].postId },
324+
});
325+
} catch (err) {}
326+
}
327+
315328
return ids.map((p) => ({
316329
...p,
317330
status: 'posted',

0 commit comments

Comments
 (0)