Skip to content

Commit

Permalink
feat(route): add Bitget crypto exchange route. (#16665)
Browse files Browse the repository at this point in the history
* feat: add new route Bitget.

* fix: excludeStationLetterType not found.

* fix: get pageSize from `limit` parameter.

* chore: use `camelCase` in func name.

* fix: use parseDate and timezone.

* fix: use `script#__NEXT_DATA__` to parse response.

* chore: rename variable name.

* chore: remove pageSize in example and description.

* chore: remove timezone offset.

* chore: more alternatives in dataItem.description.

* chore: remove unnecessary replace.

* feat: add radar.

* chore: remove timezone()

* fix: update api request tryGet() policy.

---------
  • Loading branch information
YukiCoco committed Sep 12, 2024
1 parent 3adb977 commit dbc1628
Show file tree
Hide file tree
Showing 3 changed files with 232 additions and 0 deletions.
200 changes: 200 additions & 0 deletions lib/routes/bitget/announcement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import { DataItem, Route } from '@/types';
import ofetch from '@/utils/ofetch';
import { load } from 'cheerio';
import cache from '@/utils/cache';
import { BitgetResponse } from './type';
import { parseDate } from '@/utils/parse-date';
import { config } from '@/config';

const handler: Route['handler'] = async (ctx) => {
const baseUrl = 'https://www.bitget.com';
const announcementApiUrl = `${baseUrl}/v1/msg/push/stationLetterNew`;
const { type, lang = 'zh-CN' } = ctx.req.param<'/bitget/announcement/:type/:lang?'>();
const languageCode = lang.replace('-', '_');
const headers = {
Referer: baseUrl,
accept: 'application/json, text/plain, */*',
'content-type': 'application/json;charset=UTF-8',
language: languageCode,
locale: languageCode,
};
const pageSize = ctx.req.query('limit') ?? '10';

// stationLetterType: 0 表示全部通知,02 表示新币上线,01 表示最新活动,06 表示最新公告
const reqBody: {
pageSize: string;
openUnread: number;
stationLetterType: string;
isPre: boolean;
lastEndId: null;
languageType: number;
excludeStationLetterType?: string;
} = {
pageSize,
openUnread: 0,
stationLetterType: '0',
isPre: false,
lastEndId: null,
languageType: 1,
};

// 根据 type 判断 reqBody 的 stationLetterType 的值
switch (type) {
case 'new-listing':
reqBody.stationLetterType = '02';
break;

case 'latest-activities':
reqBody.stationLetterType = '01';
break;

case 'new-announcement':
reqBody.stationLetterType = '06';
break;

case 'all':
reqBody.stationLetterType = '0';
reqBody.excludeStationLetterType = '00';
break;

default:
throw new Error('Invalid type');
}

const response = (await cache.tryGet(
`bitget:announcement:${type}:${pageSize}:${lang}`,
async () => {
const result = await ofetch<BitgetResponse>(announcementApiUrl, {
method: 'POST',
body: reqBody,
headers,
});
if (result?.code !== '200') {
throw new Error('Failed to fetch announcements, error code: ' + result?.code);
}
return result;
},
config.cache.routeExpire,
false
)) as BitgetResponse;

if (!response) {
throw new Error('Failed to fetch announcements');
}
const items = response.data.items;
const data = await Promise.all(
items.map(
(item) =>
cache.tryGet(`bitget:announcement:${item.id}:${pageSize}:${lang}`, async () => {
// 从 unix 时间戳转换为日期
const date = parseDate(Number(item.sendTime));
const dataItem: DataItem = {
title: item.title ?? '',
link: item.openUrl ?? '',
pubDate: item.sendTime ? date : undefined,
description: item.content ?? '',
};

if (item.imgUrl) {
dataItem.image = item.imgUrl;
}

if (item.stationLetterType === '01' || item.stationLetterType === '06') {
try {
const itemResponse = await ofetch<string>(item.openUrl ?? '', {
headers,
});
const $ = load(itemResponse);
const nextData = JSON.parse($('script#__NEXT_DATA__').text());
dataItem.description = nextData.props.pageProps.details?.content || nextData.props.pageProps.pageInitInfo?.ruleContent || item.content || '';
} catch (error: any) {
if (error.name && (error.name === 'HTTPError' || error.name === 'RequestError' || error.name === 'FetchError')) {
dataItem.description = item.content ?? '';
} else {
throw error;
}
}
}
return dataItem;
}) as Promise<DataItem>
)
);

return {
title: `Bitget | ${findTypeLabel(type)}`,
link: `https://www.bitget.com/${lang}/inmail`,
item: data,
};
};

const findTypeLabel = (type: string) => {
const typeMap = {
all: 'All',
'new-listing': 'New Listing',
'latest-activities': 'Latest Activities',
'new-announcement': 'New Announcement',
};
return typeMap[type];
};

export const route: Route = {
path: '/announcement/:type/:lang?',
categories: ['finance'],
example: '/bitget/announcement/all/zh-CN',
parameters: {
type: {
description: 'Bitget 通知类型',
default: 'all',
options: [
{ value: 'all', label: '全部通知' },
{ value: 'new-listing', label: '新币上线' },
{ value: 'latest-activities', label: '最新活动' },
{ value: 'new-announcement', label: '最新公告' },
],
},
lang: {
description: '语言',
default: 'zh-CN',
options: [
{ value: 'zh-CN', label: '中文' },
{ value: 'en-US', label: 'English' },
{ value: 'es-ES', label: 'Español' },
{ value: 'fr-FR', label: 'Français' },
{ value: 'de-DE', label: 'Deutsch' },
{ value: 'ja-JP', label: '日本語' },
{ value: 'ru-RU', label: 'Русский' },
{ value: 'ar-SA', label: 'العربية' },
],
},
},
radar: [
{
source: ['www.bitget.com/:lang/inmail'],
target: '/announcement/all/:lang',
},
],
name: 'Announcement',
description: `
type:
| Type | Description |
| --- | --- |
| all | 全部通知 |
| new-listing | 新币上线 |
| latest-activities | 最新活动 |
| new-announcement | 最新公告 |
lang:
| Lang | Description |
| --- | --- |
| zh-CN | 中文 |
| en-US | English |
| es-ES | Español |
| fr-FR | Français |
| de-DE | Deutsch |
| ja-JP | 日本語 |
| ru-RU | Русский |
| ar-SA | العربية |
`,
maintainers: ['YukiCoco'],
handler,
};
6 changes: 6 additions & 0 deletions lib/routes/bitget/namespace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import type { Namespace } from '@/types';

export const namespace: Namespace = {
name: 'Bitget',
url: 'bitget.com',
};
26 changes: 26 additions & 0 deletions lib/routes/bitget/type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
export interface BitgetResponse {
code: string;
data: {
endId: string;
hasNextPage: boolean;
hasPrePage: boolean;
items: Array<{
businessType?: number;
content?: string;
id: string;
imgUrl?: string;
openUrl?: string;
openUrlName?: string;
readStats?: number;
sendTime?: string;
stationLetterType?: string;
title?: string;
}>;
notifyFlag: boolean;
page: number;
pageSize: number;
startId: string;
total: number;
};
params: any[];
}

0 comments on commit dbc1628

Please sign in to comment.