diff --git a/lib/routes/weekendhk/posts.ts b/lib/routes/weekendhk/posts.ts
index 6b84ae2fdf30dd..d642a4c6c05192 100644
--- a/lib/routes/weekendhk/posts.ts
+++ b/lib/routes/weekendhk/posts.ts
@@ -4,13 +4,13 @@ import { parseDate } from '@/utils/parse-date';
export const route: Route = {
path: '/',
+ example: '/weekendhk',
radar: [
{
source: ['weekendhk.com/'],
- target: '',
},
],
- name: 'Unknown',
+ name: '最新文章',
maintainers: ['TonyRL'],
handler,
url: 'weekendhk.com/',
diff --git a/lib/routes/wellcee/namespace.ts b/lib/routes/wellcee/namespace.ts
new file mode 100644
index 00000000000000..8641c71079161b
--- /dev/null
+++ b/lib/routes/wellcee/namespace.ts
@@ -0,0 +1,7 @@
+import type { Namespace } from '@/types';
+
+export const namespace: Namespace = {
+ name: 'Wellcee 唯心所寓',
+ url: 'wellcee.com',
+ categories: ['other'],
+};
diff --git a/lib/routes/wellcee/rent.ts b/lib/routes/wellcee/rent.ts
new file mode 100644
index 00000000000000..dc67f30c91bfef
--- /dev/null
+++ b/lib/routes/wellcee/rent.ts
@@ -0,0 +1,81 @@
+import { Route } from '@/types';
+import type { Context } from 'hono';
+import { District, House } from './types';
+
+import ofetch from '@/utils/ofetch';
+import { parseDate } from '@/utils/parse-date';
+import { baseUrl, getCitys, getDistricts } from './utils';
+import InvalidParameterError from '@/errors/types/invalid-parameter';
+import { art } from '@/utils/render';
+import path from 'path';
+import { getCurrentPath } from '@/utils/helpers';
+
+const __dirname = getCurrentPath(import.meta.url);
+const render = (data) => art(path.join(__dirname, 'templates', 'house.art'), data);
+
+export const route: Route = {
+ path: '/rent/:city/:district?',
+ example: '/wellcee/rent/北京',
+ parameters: {
+ city: '城市',
+ district: '地区',
+ },
+ name: '租房信息',
+ maintainers: ['TonyRL'],
+ handler,
+ url: 'www.wellcee.com',
+ description: '支持的城市可以通过 [/wellcee/support-city](https://rsshub.app/wellcee/support-city) 获取',
+};
+
+async function handler(ctx: Context) {
+ const { city, district = '' } = ctx.req.param();
+ const citys = await getCitys();
+ const cityInfo = citys.find((item) => item.chCityName === city);
+ if (!cityInfo) {
+ throw new InvalidParameterError('Invalid city');
+ }
+
+ let districtInfo: District | undefined;
+ if (district) {
+ const districts = await getDistricts(cityInfo.id);
+ const d = districts.find((item) => item.name === district);
+ if (!d) {
+ throw new InvalidParameterError('Invalid district');
+ }
+ districtInfo = d;
+ }
+
+ const response = await ofetch(`${baseUrl}/api/house/filter`, {
+ method: 'POST',
+ body: {
+ districtIds: districtInfo?.id ? [districtInfo.id] : [],
+ subways: [],
+ rentTypeIds: [],
+ timeTypeIds: [],
+ price: [],
+ tagTypeIds: [],
+ cityId: cityInfo.id,
+ lang: 1,
+ pn: 1,
+ },
+ });
+
+ const items = (response.data.list as House[]).map((item) => ({
+ title: item.address,
+ link: `${baseUrl}/rent-apartment/${item.id}`,
+ description: render({ item }),
+ pubDate: parseDate(item.loginTime, 'X'),
+ author: item.userInfo.name,
+ category: [...item.tags, ...item.typeTags],
+ }));
+
+ return {
+ title: `${city}${districtInfo?.name ?? ''}租房信息 - Wellcee`,
+ description: `${cityInfo.statics.online_text} ${cityInfo.statics.total_text}`,
+ image: cityInfo.icon,
+ icon: cityInfo.icon,
+ logo: cityInfo.icon,
+ link: `${baseUrl}/rent-apartment/${cityInfo.cityName}/list?cityId=${cityInfo.id}&lang=zh${district ? `#districtIds=${districtInfo?.id}` : ''}`,
+ item: items,
+ };
+}
diff --git a/lib/routes/wellcee/support-city.ts b/lib/routes/wellcee/support-city.ts
new file mode 100644
index 00000000000000..2b0b88d9b729f2
--- /dev/null
+++ b/lib/routes/wellcee/support-city.ts
@@ -0,0 +1,46 @@
+import { Route } from '@/types';
+import type { Context } from 'hono';
+
+import { baseUrl, getCitys, getDistricts } from './utils';
+
+export const route: Route = {
+ path: '/support-city',
+ example: '/wellcee/support-city',
+ name: '支持的城市',
+ maintainers: ['TonyRL'],
+ radar: [
+ {
+ source: ['www.wellcee.com'],
+ },
+ ],
+ handler,
+ url: 'www.wellcee.com',
+};
+
+async function handler(ctx: Context) {
+ const citys = await getCitys();
+
+ const list = await Promise.all(
+ citys.map(async (city) => ({
+ ...city,
+ district: await getDistricts(city.id),
+ }))
+ );
+ const requestHost = new URL(ctx.req.url).host;
+
+ const items = list.flatMap((city) =>
+ city.district.map((district) => ({
+ title: `${city.chCityName} - ${district.name}`,
+ description: `${city.chCityName} - ${district.name}`,
+ link: `https://${requestHost}/wellcee/rent/${city.chCityName}/${district.name}`,
+ }))
+ );
+
+ return {
+ title: '支持的城市 - Wellcee',
+ description:
+ '上海国际化租房平台|北京合租&找室友|香港留学生租房|深圳无中介租房|广州外国人租房 |杭州高品质租房|成都房东直租;同志友好&宠物友好;Wellcee 的生活方式:社交|活动|交友|美食|宠物领养|音乐&艺术;Wellcee 的二手市集:家居|电子|奢侈品|时尚。',
+ link: baseUrl,
+ item: items,
+ };
+}
diff --git a/lib/routes/wellcee/templates/house.art b/lib/routes/wellcee/templates/house.art
new file mode 100644
index 00000000000000..2fb3b737931dc7
--- /dev/null
+++ b/lib/routes/wellcee/templates/house.art
@@ -0,0 +1,18 @@
+租金: {{ item.rent }}
+{{ if item.dailyRent }}
+日租: {{ item.dailyRent }}
+{{ /if }}
+
+
+{{ if item.video }}
+
+
+{{ /if }}
+
+{{ if item.imgs }}
+ {{ each item.imgs img }}
+
+ {{ /each }}
+{{ /if }}
diff --git a/lib/routes/wellcee/types.ts b/lib/routes/wellcee/types.ts
new file mode 100644
index 00000000000000..9e718a0087066c
--- /dev/null
+++ b/lib/routes/wellcee/types.ts
@@ -0,0 +1,58 @@
+export interface City {
+ id: string;
+ icon: string;
+ city: string;
+ enCityName: string;
+ cityName: string;
+ chCityName: string;
+ twCityName: string;
+ statics: {
+ total_text: string;
+ online_text: string;
+ total_count: number;
+ online_count: number;
+ };
+ district: District[];
+}
+
+export interface District {
+ id: string;
+ longitude: number;
+ latitude: number;
+ name: string;
+ business: {
+ id: string;
+ name: string;
+ }[];
+}
+
+export interface House {
+ id: string;
+ imgs: string[];
+ ev: number;
+ city: string;
+ district: string;
+ tagsText: string;
+ tags: string[];
+ typeTags: string[];
+ rent: string;
+ video: string;
+ personalDesc: string;
+ address: string;
+ longitude: number;
+ latitude: number;
+ collectNum: number;
+ isCollect: boolean;
+ userInfo: {
+ id: string;
+ name: string;
+ gender: number;
+ avatar: string;
+ language: string[];
+ loginTime: string;
+ tenant_status: number;
+ landlord_status: number;
+ };
+ loginTime: number;
+ dailyRent: string;
+}
diff --git a/lib/routes/wellcee/utils.ts b/lib/routes/wellcee/utils.ts
new file mode 100644
index 00000000000000..f10baf402c8abd
--- /dev/null
+++ b/lib/routes/wellcee/utils.ts
@@ -0,0 +1,45 @@
+import { City, District } from './types';
+
+import ofetch from '@/utils/ofetch';
+import cache from '@/utils/cache';
+import { config } from '@/config';
+
+export const baseUrl = 'https://www.wellcee.com';
+export const getCitys = () =>
+ cache.tryGet(
+ 'wellcee:citys',
+ async () => {
+ const response = await ofetch(`${baseUrl}/api/home/index`, {
+ method: 'POST',
+ headers: {
+ 'content-type': 'application/x-www-form-urlencoded',
+ },
+ body: new URLSearchParams({
+ lang: '1',
+ userId: '',
+ type: '1',
+ }).toString(),
+ });
+
+ return response.data.citys;
+ },
+ config.cache.routeExpire,
+ false
+ ) as Promise;
+
+export const getDistricts = (cityId: string) =>
+ cache.tryGet(
+ `wellcee:city:${cityId}`,
+ async () => {
+ const response = await ofetch(`${baseUrl}/api/house/filterType`, {
+ query: {
+ cityId,
+ lang: '1',
+ },
+ });
+
+ return response.data.district;
+ },
+ config.cache.routeExpire,
+ false
+ ) as Promise;