feat: long tweet for X + add additional settings per provider
This commit is contained in:
parent
4806b82c20
commit
653f787b72
|
|
@ -106,11 +106,24 @@ export class IntegrationsController {
|
|||
changeProfilePicture: !!findIntegration?.changeProfilePicture,
|
||||
changeNickName: !!findIntegration?.changeNickname,
|
||||
customer: p.customer,
|
||||
additionalSettings: p.additionalSettings || '[]',
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@Post('/:id/settings')
|
||||
async updateProviderSettings(
|
||||
@GetOrgFromRequest() org: Organization,
|
||||
@Param('id') id: string,
|
||||
@Body('additionalSettings') body: string
|
||||
) {
|
||||
if (typeof body !== 'string') {
|
||||
throw new Error('Invalid body');
|
||||
}
|
||||
|
||||
await this._integrationService.updateProviderSettings(org.id, id, body);
|
||||
}
|
||||
@Post('/:id/nickname')
|
||||
async setNickname(
|
||||
@GetOrgFromRequest() org: Organization,
|
||||
|
|
@ -257,13 +270,14 @@ export class IntegrationsController {
|
|||
return load;
|
||||
} catch (err) {
|
||||
if (err instanceof RefreshToken) {
|
||||
const { accessToken, refreshToken, expiresIn } =
|
||||
const { accessToken, refreshToken, expiresIn, additionalSettings } =
|
||||
await integrationProvider.refreshToken(
|
||||
getIntegration.refreshToken
|
||||
);
|
||||
|
||||
if (accessToken) {
|
||||
await this._integrationService.createOrUpdateIntegration(
|
||||
additionalSettings,
|
||||
!!integrationProvider.oneTimeToken,
|
||||
getIntegration.organizationId,
|
||||
getIntegration.name,
|
||||
|
|
@ -346,6 +360,7 @@ export class IntegrationsController {
|
|||
}
|
||||
|
||||
return this._integrationService.createOrUpdateIntegration(
|
||||
undefined,
|
||||
true,
|
||||
org.id,
|
||||
name,
|
||||
|
|
@ -413,6 +428,7 @@ export class IntegrationsController {
|
|||
name,
|
||||
picture,
|
||||
username,
|
||||
additionalSettings,
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
} = await new Promise<AuthTokenDetails>(async (res) => {
|
||||
const auth = await integrationProvider.authenticate(
|
||||
|
|
@ -432,6 +448,7 @@ export class IntegrationsController {
|
|||
name: '',
|
||||
picture: '',
|
||||
username: '',
|
||||
additionalSettings: [],
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -470,6 +487,7 @@ export class IntegrationsController {
|
|||
}
|
||||
}
|
||||
return this._integrationService.createOrUpdateIntegration(
|
||||
additionalSettings,
|
||||
!!integrationProvider.oneTimeToken,
|
||||
org.id,
|
||||
validName.trim(),
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ export interface Integrations {
|
|||
type: string;
|
||||
picture: string;
|
||||
changeProfilePicture: boolean;
|
||||
additionalSettings: string;
|
||||
changeNickName: boolean;
|
||||
time: { time: number }[];
|
||||
customer?: {
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -87,7 +87,7 @@ export const withProvider = function <T extends object>(
|
|||
value: Array<Array<{ path: string }>>,
|
||||
settings: T
|
||||
) => Promise<string | true>,
|
||||
maximumCharacters?: number
|
||||
maximumCharacters?: number | ((settings: any) => number)
|
||||
) {
|
||||
return (props: {
|
||||
identifier: string;
|
||||
|
|
@ -155,7 +155,11 @@ export const withProvider = function <T extends object>(
|
|||
editInPlace ? InPlaceValue : props.value,
|
||||
dto,
|
||||
checkValidity,
|
||||
maximumCharacters
|
||||
!maximumCharacters
|
||||
? undefined
|
||||
: typeof maximumCharacters === 'number'
|
||||
? maximumCharacters
|
||||
: maximumCharacters(JSON.parse(integration?.additionalSettings || '[]'))
|
||||
);
|
||||
|
||||
// change editor value
|
||||
|
|
@ -348,10 +352,12 @@ export const withProvider = function <T extends object>(
|
|||
);
|
||||
|
||||
const getInternalPlugs = useCallback(async () => {
|
||||
return (await fetch(`/integrations/${props.identifier}/internal-plugs`)).json();
|
||||
return (
|
||||
await fetch(`/integrations/${props.identifier}/internal-plugs`)
|
||||
).json();
|
||||
}, [props.identifier]);
|
||||
|
||||
const {data} = useSWR(`internal-${props.identifier}`, getInternalPlugs);
|
||||
const { data } = useSWR(`internal-${props.identifier}`, getInternalPlugs);
|
||||
|
||||
// this is a trick to prevent the data from being deleted, yet we don't render the elements
|
||||
if (!props.show) {
|
||||
|
|
@ -423,7 +429,8 @@ export const withProvider = function <T extends object>(
|
|||
<div>
|
||||
<div className="flex gap-[4px]">
|
||||
<div className="flex-1 text-textColor editor">
|
||||
{(integration?.identifier === 'linkedin' || integration?.identifier === 'linkedin-page') && (
|
||||
{(integration?.identifier === 'linkedin' ||
|
||||
integration?.identifier === 'linkedin-page') && (
|
||||
<Button
|
||||
className="mb-[5px]"
|
||||
onClick={tagPersonOrCompany(
|
||||
|
|
@ -527,7 +534,7 @@ export const withProvider = function <T extends object>(
|
|||
<div className={clsx('mt-[20px]', showTab !== 2 && 'hidden')}>
|
||||
<Component values={editInPlace ? InPlaceValue : props.value} />
|
||||
{data?.internalPlugs?.length && (
|
||||
<InternalChannels plugs={data?.internalPlugs} />
|
||||
<InternalChannels plugs={data?.internalPlugs} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
|
@ -546,11 +553,23 @@ export const withProvider = function <T extends object>(
|
|||
.join('').length ? (
|
||||
CustomPreviewComponent ? (
|
||||
<CustomPreviewComponent
|
||||
maximumCharacters={maximumCharacters}
|
||||
maximumCharacters={
|
||||
!maximumCharacters
|
||||
? undefined
|
||||
: typeof maximumCharacters === 'number'
|
||||
? maximumCharacters
|
||||
: maximumCharacters(JSON.parse(integration?.additionalSettings || '[]'))
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<GeneralPreviewComponent
|
||||
maximumCharacters={maximumCharacters}
|
||||
maximumCharacters={
|
||||
!maximumCharacters
|
||||
? undefined
|
||||
: typeof maximumCharacters === 'number'
|
||||
? maximumCharacters
|
||||
: maximumCharacters(JSON.parse(integration?.additionalSettings || '[]'))
|
||||
}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -26,7 +26,13 @@ export default withProvider(
|
|||
}
|
||||
return true;
|
||||
},
|
||||
280
|
||||
(settings) => {
|
||||
if (settings?.[0]?.value) {
|
||||
console.log(4000);
|
||||
return 4000;
|
||||
}
|
||||
return 280;
|
||||
}
|
||||
);
|
||||
|
||||
const checkVideoDuration = async (url: string): Promise<boolean> => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,96 @@
|
|||
import { TopTitle } from '@gitroom/frontend/components/launches/helpers/top.title.component';
|
||||
import React, { FC, useCallback, useMemo, useState } from 'react';
|
||||
import { useModals } from '@mantine/modals';
|
||||
import { Integration } from '@prisma/client';
|
||||
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
|
||||
import { Button } from '@gitroom/react/form/button';
|
||||
import { Slider } from '@gitroom/react/form/slider';
|
||||
|
||||
export const Element: FC<{ setting: any; onChange: (value: any) => void }> = (
|
||||
props
|
||||
) => {
|
||||
const { setting, onChange } = props;
|
||||
const [value, setValue] = useState(setting.value);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-[10px]">
|
||||
<div>{setting.title}</div>
|
||||
<div className="text-[14px]">{setting.description}</div>
|
||||
<Slider
|
||||
value={value === true ? 'on' : 'off'}
|
||||
onChange={() => {
|
||||
setValue(!value);
|
||||
onChange(!value);
|
||||
}}
|
||||
fill={true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const SettingsModal: FC<{
|
||||
integration: Integration & { customer?: { id: string; name: string } };
|
||||
onClose: () => void;
|
||||
}> = (props) => {
|
||||
const fetch = useFetch();
|
||||
const { onClose, integration } = props;
|
||||
const modal = useModals();
|
||||
const [values, setValues] = useState(
|
||||
JSON.parse(integration?.additionalSettings || '[]')
|
||||
);
|
||||
|
||||
const changeValue = useCallback(
|
||||
(index: number) => (value: any) => {
|
||||
const newValues = [...values];
|
||||
newValues[index].value = value;
|
||||
setValues(newValues);
|
||||
},
|
||||
[values]
|
||||
);
|
||||
|
||||
const save = useCallback(async () => {
|
||||
await fetch(`/integrations/${integration.id}/settings`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ additionalSettings: JSON.stringify(values) }),
|
||||
});
|
||||
|
||||
modal.closeAll();
|
||||
onClose();
|
||||
}, [values, integration]);
|
||||
|
||||
return (
|
||||
<div className="rounded-[4px] border border-customColor6 bg-sixth px-[16px] pb-[16px] relative w-full">
|
||||
<TopTitle title={`Additional Settings`} />
|
||||
<button
|
||||
className="outline-none absolute right-[20px] top-[20px] mantine-UnstyledButton-root mantine-ActionIcon-root hover:bg-tableBorder cursor-pointer mantine-Modal-close mantine-1dcetaa"
|
||||
type="button"
|
||||
onClick={() => modal.closeAll()}
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 15 15"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
>
|
||||
<path
|
||||
d="M11.7816 4.03157C12.0062 3.80702 12.0062 3.44295 11.7816 3.2184C11.5571 2.99385 11.193 2.99385 10.9685 3.2184L7.50005 6.68682L4.03164 3.2184C3.80708 2.99385 3.44301 2.99385 3.21846 3.2184C2.99391 3.44295 2.99391 3.80702 3.21846 4.03157L6.68688 7.49999L3.21846 10.9684C2.99391 11.193 2.99391 11.557 3.21846 11.7816C3.44301 12.0061 3.80708 12.0061 4.03164 11.7816L7.50005 8.31316L10.9685 11.7816C11.193 12.0061 11.5571 12.0061 11.7816 11.7816C12.0062 11.557 12.0062 11.193 11.7816 10.9684L8.31322 7.49999L11.7816 4.03157Z"
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
></path>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<div className="mt-[16px]">
|
||||
{values.map((setting: any, index: number) => (
|
||||
<Element key={setting.title} setting={setting} onChange={changeValue(index)} />
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="my-[16px] flex gap-[10px]">
|
||||
<Button onClick={save}>Save</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -4,7 +4,7 @@ import twitter from 'twitter-text';
|
|||
export const textSlicer = (
|
||||
integrationType: string,
|
||||
end: number,
|
||||
text: string
|
||||
text: string,
|
||||
): {start: number, end: number} => {
|
||||
if (integrationType !== 'x') {
|
||||
return {
|
||||
|
|
@ -13,7 +13,21 @@ export const textSlicer = (
|
|||
}
|
||||
}
|
||||
|
||||
const {validRangeEnd, valid} = twitter.parseTweet(text);
|
||||
const {validRangeEnd, valid} = twitter.parseTweet(text, {
|
||||
version: 3,
|
||||
maxWeightedTweetLength: end,
|
||||
scale: 100,
|
||||
defaultWeight: 200,
|
||||
emojiParsingEnabled: true,
|
||||
transformedURLLength: 23,
|
||||
ranges: [
|
||||
{ start: 0, end: 4351, weight: 100 },
|
||||
{ start: 8192, end: 8205, weight: 100 },
|
||||
{ start: 8208, end: 8223, weight: 100 },
|
||||
{ start: 8242, end: 8247, weight: 100 }
|
||||
]
|
||||
});
|
||||
|
||||
return {
|
||||
start: 0,
|
||||
end: valid ? end : validRangeEnd
|
||||
|
|
|
|||
|
|
@ -18,6 +18,18 @@ export class IntegrationRepository {
|
|||
private _customers: PrismaRepository<'customer'>
|
||||
) {}
|
||||
|
||||
updateProviderSettings(org: string, id: string, settings: string) {
|
||||
return this._integration.model.integration.update({
|
||||
where: {
|
||||
id,
|
||||
organizationId: org,
|
||||
},
|
||||
data: {
|
||||
additionalSettings: settings,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async setTimes(org: string, id: string, times: IntegrationTimeDto) {
|
||||
return this._integration.model.integration.update({
|
||||
select: {
|
||||
|
|
@ -94,6 +106,15 @@ export class IntegrationRepository {
|
|||
}
|
||||
|
||||
async createOrUpdateIntegration(
|
||||
additionalSettings:
|
||||
| {
|
||||
title: string;
|
||||
description: string;
|
||||
type: 'checkbox' | 'text' | 'textarea';
|
||||
value: any;
|
||||
regex?: string;
|
||||
}[]
|
||||
| undefined,
|
||||
oneTimeToken: boolean,
|
||||
org: string,
|
||||
name: string,
|
||||
|
|
@ -144,6 +165,9 @@ export class IntegrationRepository {
|
|||
refreshNeeded: false,
|
||||
rootInternalId: internalId.split('_').pop(),
|
||||
...(customInstanceDetails ? { customInstanceDetails } : {}),
|
||||
additionalSettings: additionalSettings
|
||||
? JSON.stringify(additionalSettings)
|
||||
: '[]',
|
||||
},
|
||||
update: {
|
||||
type: type as any,
|
||||
|
|
@ -168,17 +192,28 @@ export class IntegrationRepository {
|
|||
});
|
||||
|
||||
if (oneTimeToken) {
|
||||
const rootId =
|
||||
(
|
||||
await this._integration.model.integration.findFirst({
|
||||
where: {
|
||||
organizationId: org,
|
||||
internalId: internalId,
|
||||
},
|
||||
})
|
||||
)?.rootInternalId || internalId.split('_').pop()!;
|
||||
|
||||
await this._integration.model.integration.updateMany({
|
||||
where: {
|
||||
id: {
|
||||
not: upsert.id,
|
||||
},
|
||||
organizationId: org,
|
||||
rootInternalId: internalId.split('_').pop(),
|
||||
rootInternalId: rootId,
|
||||
},
|
||||
data: {
|
||||
token,
|
||||
refreshToken,
|
||||
refreshNeeded: false,
|
||||
...(expiresIn
|
||||
? { tokenExpiration: new Date(Date.now() + expiresIn * 1000) }
|
||||
: {}),
|
||||
|
|
|
|||
|
|
@ -41,7 +41,26 @@ export class IntegrationService {
|
|||
return this._integrationRepository.setTimes(orgId, integrationId, times);
|
||||
}
|
||||
|
||||
updateProviderSettings(
|
||||
org: string,
|
||||
id: string,
|
||||
additionalSettings: string
|
||||
) {
|
||||
return this._integrationRepository.updateProviderSettings(
|
||||
org,
|
||||
id,
|
||||
additionalSettings
|
||||
);
|
||||
}
|
||||
|
||||
async createOrUpdateIntegration(
|
||||
additionalSettings: {
|
||||
title: string;
|
||||
description: string;
|
||||
type: 'checkbox' | 'text' | 'textarea';
|
||||
value: any;
|
||||
regex?: string;
|
||||
}[] | undefined,
|
||||
oneTimeToken: boolean,
|
||||
org: string,
|
||||
name: string,
|
||||
|
|
@ -62,6 +81,7 @@ export class IntegrationService {
|
|||
? await this.storage.uploadSimple(picture)
|
||||
: undefined;
|
||||
return this._integrationRepository.createOrUpdateIntegration(
|
||||
additionalSettings,
|
||||
oneTimeToken,
|
||||
org,
|
||||
name,
|
||||
|
|
@ -166,6 +186,7 @@ export class IntegrationService {
|
|||
const { refreshToken, accessToken, expiresIn } = data;
|
||||
|
||||
await this.createOrUpdateIntegration(
|
||||
undefined,
|
||||
!!provider.oneTimeToken,
|
||||
integration.organizationId,
|
||||
integration.name,
|
||||
|
|
@ -334,7 +355,7 @@ export class IntegrationService {
|
|||
dayjs(getIntegration?.tokenExpiration).isBefore(dayjs()) ||
|
||||
forceRefresh
|
||||
) {
|
||||
const { accessToken, expiresIn, refreshToken } =
|
||||
const { accessToken, expiresIn, refreshToken, additionalSettings } =
|
||||
await new Promise<AuthTokenDetails>((res) => {
|
||||
return integrationProvider
|
||||
.refreshToken(getIntegration.refreshToken!)
|
||||
|
|
@ -347,12 +368,14 @@ export class IntegrationService {
|
|||
name: '',
|
||||
picture: '',
|
||||
username: '',
|
||||
additionalSettings: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (accessToken) {
|
||||
await this.createOrUpdateIntegration(
|
||||
additionalSettings,
|
||||
!!integrationProvider.oneTimeToken,
|
||||
getIntegration.organizationId,
|
||||
getIntegration.name,
|
||||
|
|
@ -429,10 +452,11 @@ export class IntegrationService {
|
|||
delay: number;
|
||||
information: any;
|
||||
}) {
|
||||
const originalIntegration = await this._integrationRepository.getIntegrationById(
|
||||
data.orgId,
|
||||
data.originalIntegration
|
||||
);
|
||||
const originalIntegration =
|
||||
await this._integrationRepository.getIntegrationById(
|
||||
data.orgId,
|
||||
data.originalIntegration
|
||||
);
|
||||
|
||||
const getIntegration = await this._integrationRepository.getIntegrationById(
|
||||
data.orgId,
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ export class PostsService {
|
|||
}
|
||||
|
||||
if (dayjs(integration?.tokenExpiration).isBefore(dayjs()) || forceRefresh) {
|
||||
const { accessToken, expiresIn, refreshToken } =
|
||||
const { accessToken, expiresIn, refreshToken, additionalSettings } =
|
||||
await new Promise<AuthTokenDetails>((res) => {
|
||||
getIntegration
|
||||
.refreshToken(integration.refreshToken!)
|
||||
|
|
@ -231,6 +231,7 @@ export class PostsService {
|
|||
name: '',
|
||||
username: '',
|
||||
picture: '',
|
||||
additionalSettings: undefined,
|
||||
})
|
||||
);
|
||||
});
|
||||
|
|
@ -249,6 +250,7 @@ export class PostsService {
|
|||
}
|
||||
|
||||
await this._integrationService.createOrUpdateIntegration(
|
||||
additionalSettings,
|
||||
!!getIntegration.oneTimeToken,
|
||||
integration.organizationId,
|
||||
integration.name,
|
||||
|
|
|
|||
|
|
@ -288,6 +288,7 @@ model Integration {
|
|||
plugs Plugs[]
|
||||
exisingPlugData ExisingPlugData[]
|
||||
rootInternalId String?
|
||||
additionalSettings String? @default("[]")
|
||||
|
||||
@@index([rootInternalId])
|
||||
@@index([updatedAt])
|
||||
|
|
|
|||
|
|
@ -49,8 +49,6 @@ export class LinkedinProvider extends SocialAbstract implements SocialProvider {
|
|||
})
|
||||
).json();
|
||||
|
||||
console.log('refreshToken', refreshToken);
|
||||
|
||||
const { vanityName } = await (
|
||||
await this.fetch('https://api.linkedin.com/v2/me', {
|
||||
headers: {
|
||||
|
|
|
|||
|
|
@ -13,9 +13,13 @@ export interface IAuthenticator {
|
|||
refresh?: string;
|
||||
},
|
||||
clientInformation?: ClientInformation
|
||||
): Promise<AuthTokenDetails|string>;
|
||||
): Promise<AuthTokenDetails | string>;
|
||||
refreshToken(refreshToken: string): Promise<AuthTokenDetails>;
|
||||
reConnect?(id: string, requiredId: string, accessToken: string): Promise<AuthTokenDetails>;
|
||||
reConnect?(
|
||||
id: string,
|
||||
requiredId: string,
|
||||
accessToken: string
|
||||
): Promise<AuthTokenDetails>;
|
||||
generateAuthUrl(
|
||||
clientInformation?: ClientInformation
|
||||
): Promise<GenerateAuthUrlResponse>;
|
||||
|
|
@ -57,6 +61,13 @@ export type AuthTokenDetails = {
|
|||
expiresIn?: number; // The duration in seconds for which the access token is valid
|
||||
picture?: string;
|
||||
username: string;
|
||||
additionalSettings?: {
|
||||
title: string;
|
||||
description: string;
|
||||
type: 'checkbox' | 'text' | 'textarea';
|
||||
value: any,
|
||||
regex?: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
export interface ISocialMediaIntegration {
|
||||
|
|
|
|||
|
|
@ -86,7 +86,9 @@ export class XProvider extends SocialAbstract implements SocialProvider {
|
|||
accessSecret: accessSecretSplit,
|
||||
});
|
||||
|
||||
const {data: {id}} = await client.v2.me();
|
||||
const {
|
||||
data: { id },
|
||||
} = await client.v2.me();
|
||||
|
||||
try {
|
||||
await client.v2.retweet(id, postId);
|
||||
|
|
@ -153,35 +155,15 @@ export class XProvider extends SocialAbstract implements SocialProvider {
|
|||
return false;
|
||||
}
|
||||
|
||||
async refreshToken(refreshToken: string): Promise<AuthTokenDetails> {
|
||||
const startingClient = new TwitterApi({
|
||||
appKey: process.env.X_API_KEY!,
|
||||
appSecret: process.env.X_API_SECRET!,
|
||||
});
|
||||
const {
|
||||
accessToken,
|
||||
refreshToken: newRefreshToken,
|
||||
expiresIn,
|
||||
client,
|
||||
} = await startingClient.refreshOAuth2Token(refreshToken);
|
||||
const {
|
||||
data: { id, name, profile_image_url },
|
||||
} = await client.v2.me();
|
||||
|
||||
const {
|
||||
data: { username },
|
||||
} = await client.v2.me({
|
||||
'user.fields': 'username',
|
||||
});
|
||||
|
||||
async refreshToken(): Promise<AuthTokenDetails> {
|
||||
return {
|
||||
id,
|
||||
name,
|
||||
accessToken,
|
||||
refreshToken: newRefreshToken,
|
||||
expiresIn,
|
||||
picture: profile_image_url,
|
||||
username,
|
||||
id: '',
|
||||
name: '',
|
||||
accessToken: '',
|
||||
refreshToken: '',
|
||||
expiresIn: 0,
|
||||
picture: '',
|
||||
username: '',
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -217,18 +199,13 @@ export class XProvider extends SocialAbstract implements SocialProvider {
|
|||
accessSecret: oauth_token_secret,
|
||||
});
|
||||
|
||||
const { accessToken, client, accessSecret, userId } = await startingClient.login(
|
||||
code
|
||||
);
|
||||
|
||||
const { id, name, profile_image_url_https } = await client.currentUser(
|
||||
true
|
||||
);
|
||||
const { accessToken, client, accessSecret } =
|
||||
await startingClient.login(code);
|
||||
|
||||
const {
|
||||
data: { username },
|
||||
data: { username, verified, profile_image_url, name, id },
|
||||
} = await client.v2.me({
|
||||
'user.fields': 'username',
|
||||
'user.fields': ['username', 'verified', 'verified_type', 'profile_image_url', 'name'],
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
@ -237,8 +214,16 @@ export class XProvider extends SocialAbstract implements SocialProvider {
|
|||
name,
|
||||
refreshToken: '',
|
||||
expiresIn: 999999999,
|
||||
picture: profile_image_url_https,
|
||||
picture: profile_image_url,
|
||||
username,
|
||||
additionalSettings: [
|
||||
{
|
||||
title: 'Verified',
|
||||
description: 'Is this a verified user? (Premium)',
|
||||
type: 'checkbox' as const,
|
||||
value: verified,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue