diff --git a/lib/routes/ebay/namespace.ts b/lib/routes/ebay/namespace.ts
new file mode 100644
index 000000000000..e00a101c98c0
--- /dev/null
+++ b/lib/routes/ebay/namespace.ts
@@ -0,0 +1,8 @@
+import type { Namespace } from '@/types';
+
+export const namespace: Namespace = {
+ name: 'eBay',
+ url: 'ebay.com',
+ categories: ['shopping'],
+ description: 'eBay search results and user listings.',
+};
diff --git a/lib/routes/ebay/search.ts b/lib/routes/ebay/search.ts
new file mode 100644
index 000000000000..254e29b23279
--- /dev/null
+++ b/lib/routes/ebay/search.ts
@@ -0,0 +1,72 @@
+import { load } from 'cheerio';
+
+import type { Route } from '@/types';
+import logger from '@/utils/logger';
+import ofetch from '@/utils/ofetch';
+
+export const route: Route = {
+ path: '/search/:keywords',
+ categories: ['shopping'],
+ example: '/ebay/search/sodimm+ddr4+16gb',
+ parameters: { keywords: 'Keywords for search' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: [
+ {
+ source: ['ebay.com/sch/i.html'],
+ target: (params, url) => {
+ const searchKeywords = new URL(url).searchParams.get('_nkw');
+ return `/ebay/search/${searchKeywords}`;
+ },
+ },
+ ],
+ name: 'Search Results',
+ maintainers: ['phoeagon'],
+ handler: async (ctx) => {
+ const { keywords } = ctx.req.param();
+ const url = `https://www.ebay.com/sch/i.html?_nkw=${encodeURIComponent(keywords)}&_sop=10&_ipg=240`;
+
+ const response = await ofetch(url);
+ const $ = load(response);
+
+ const items = $('.srp-results .s-item, .srp-results .s-card, .s-card')
+ .toArray()
+ .map((item) => {
+ const $item = $(item);
+ const titleElement = $item.find('.s-item__title, .s-card__title, .s-item__title--has-tags');
+ const title = titleElement.text().replace(/^new listing/i, '').trim();
+ const link = $item.find('.s-item__link, .s-card__link').attr('href');
+ const price = $item.find('.s-item__price, .s-card__price').text().trim();
+ const imageElement = $item.find('.s-item__image-img img, img.s-item__image-img, .s-item__image-wrapper img, .s-card__image-img img, .s-item__image img, .s-card__link img');
+ const image = imageElement.attr('data-src') || imageElement.attr('src');
+
+ if (!title || !link || title.toLowerCase().includes('shop on ebay') || price === '') {
+ return null;
+ }
+
+ const cleanedLink = new URL(link);
+ cleanedLink.search = '';
+
+ return {
+ title: `${title} - ${price}`,
+ link: cleanedLink.toString(),
+ description: `})
Price: ${price}`,
+ };
+ })
+ .filter(Boolean);
+
+ logger.info(`Found ${items.length} items on eBay`);
+
+ return {
+ title: `eBay Search: ${keywords}`,
+ link: url,
+ item: items,
+ };
+ },
+};
diff --git a/lib/routes/ebay/user.ts b/lib/routes/ebay/user.ts
new file mode 100644
index 000000000000..5f511fe4d4ed
--- /dev/null
+++ b/lib/routes/ebay/user.ts
@@ -0,0 +1,65 @@
+import { load } from 'cheerio';
+
+import type { Route } from '@/types';
+import ofetch from '@/utils/ofetch';
+
+export const route: Route = {
+ path: ['/user/:username'],
+ categories: ['shopping'],
+ example: '/ebay/user/m.trotters',
+ parameters: { username: 'Username of the seller' },
+ features: {
+ requireConfig: false,
+ requirePuppeteer: false,
+ antiCrawler: false,
+ supportBT: false,
+ supportPodcast: false,
+ supportScihub: false,
+ },
+ radar: [
+ {
+ source: ['ebay.com/usr/'],
+ target: '/user/:username',
+ },
+ ],
+ name: 'User Listings',
+ maintainers: ['phoeagon'],
+ handler: async (ctx) => {
+ const { username } = ctx.req.param();
+ const url = `https://www.ebay.com/usr/${username}`;
+
+ const response = await ofetch(url);
+ const $ = load(response);
+
+ const items = $('article.str-item-card.StoreFrontItemCard')
+ .toArray()
+ .map((item) => {
+ const $item = $(item);
+ const title = $item.find('.str-card-title .str-text-span').first().text().trim();
+ const link = $item.find('.str-item-card__link').attr('href');
+ const price = $item.find('.str-item-card__property-displayPrice').text().trim();
+ const image = $item.find('.str-image img').attr('src');
+
+ if (!title || !link) {
+ return null;
+ }
+
+ const cleanedLink = new URL(link);
+ cleanedLink.search = '';
+
+ return {
+ title: `${title} - ${price}`,
+ link: cleanedLink.toString(),
+ description: `})
Price: ${price}`,
+ author: username,
+ };
+ })
+ .filter(Boolean);
+
+ return {
+ title: `eBay User: ${username}`,
+ link: url,
+ item: items,
+ };
+ },
+};