diff --git a/docs/en/new-media.md b/docs/en/new-media.md
index e34b899280..40b591c33e 100644
--- a/docs/en/new-media.md
+++ b/docs/en/new-media.md
@@ -385,6 +385,20 @@ Provides a better reading experience (full text articles) over the official one.
+## Line
+
+### Today
+
+
+
+Edition
+
+| Taiwan | Thailand | Indonesia | Hong Kong |
+| ------ | -------- | --------- | --------- |
+| tw | th | id | hk |
+
+
+
## MakeUseOf
diff --git a/docs/new-media.md b/docs/new-media.md
index bd112ec498..cf9e968b87 100644
--- a/docs/new-media.md
+++ b/docs/new-media.md
@@ -627,6 +627,20 @@ Tag
+## Line
+
+### Today
+
+
+
+Edition
+
+| Taiwan | Thailand | Indonesia | Hong Kong |
+| ------ | -------- | --------- | --------- |
+| tw | th | id | hk |
+
+
+
## MakeUseOf
diff --git a/lib/v2/line/maintainer.js b/lib/v2/line/maintainer.js
new file mode 100644
index 0000000000..ed80783719
--- /dev/null
+++ b/lib/v2/line/maintainer.js
@@ -0,0 +1,3 @@
+module.exports = {
+ '/today/:edition?/:tab?': ['nczitzk'],
+};
diff --git a/lib/v2/line/radar.js b/lib/v2/line/radar.js
new file mode 100644
index 0000000000..cbd04d1eb2
--- /dev/null
+++ b/lib/v2/line/radar.js
@@ -0,0 +1,13 @@
+module.exports = {
+ 'line.me': {
+ _name: 'LINE Today',
+ '.': [
+ {
+ title: 'LINE Today',
+ docs: 'https://docs.rsshub.app/new-media.html#line-today',
+ source: ['/'],
+ target: '/line/today/:edition?/:tab?',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/line/router.js b/lib/v2/line/router.js
new file mode 100644
index 0000000000..5ea3e1edc9
--- /dev/null
+++ b/lib/v2/line/router.js
@@ -0,0 +1,3 @@
+module.exports = function (router) {
+ router.get('/today/:edition?/:tab?', require('./today'));
+};
diff --git a/lib/v2/line/today.js b/lib/v2/line/today.js
new file mode 100644
index 0000000000..aedf74b7c6
--- /dev/null
+++ b/lib/v2/line/today.js
@@ -0,0 +1,80 @@
+const got = require('@/utils/got');
+const cheerio = require('cheerio');
+const { parseDate } = require('@/utils/parse-date');
+
+module.exports = async (ctx) => {
+ const edition = ctx.params.edition || 'tw';
+ const tab = ctx.params.tab || 'top';
+
+ const rootUrl = 'https://today.line.me';
+ const tabUrl = `${rootUrl}/api/v6/portals/${edition}/page/${tab}`;
+ const recommendationUrl = `${rootUrl}/webapi/api/v6/recommendation/articles/listings/mytoday_rec:id?offset=0&length=50&country=${edition}&gender=&age=&excludeNoThumbnail=0&containMainSnapshot=0`;
+ const currentUrl = `${rootUrl}/${edition}/v2/tab/${tab}`;
+
+ let title = 'Recommendation',
+ moduleUrl;
+
+ if (tab !== 'recommendation') {
+ const moduleResponse = await got({
+ method: 'get',
+ url: tabUrl,
+ });
+
+ const listing = moduleResponse.data.modules.filter((item) => item.source === 'CATEGORY_MOST_VIEW')[0].listings[0];
+
+ title = moduleResponse.data.name;
+ moduleUrl =
+ `${rootUrl}/webapi/trending/category/mostView/listings/${listing.id}?` +
+ `offset=0&length=50&country=${edition}&targetContent=${listing.params.targetContent}` +
+ `&categories=${listing.params.categories}&trendingEventPeriod=${listing.params.trendingEventPeriod}`;
+ }
+
+ const response = await got({
+ method: 'get',
+ url: tab === 'recommendation' ? recommendationUrl : moduleUrl,
+ });
+
+ const list = response.data.items.map((item) => ({
+ title: item.title,
+ link: `${rootUrl}/${edition}/v2/article/${item.url.hash}`,
+ pubDate: parseDate(item.publishTimeUnix),
+ }));
+
+ const items = await Promise.all(
+ list.map((item) =>
+ ctx.cache.tryGet(item.link, async () => {
+ const detailResponse = await got({
+ method: 'get',
+ url: item.link,
+ });
+
+ const content = cheerio.load(detailResponse.data);
+
+ content('img').each(function () {
+ content(this).parent().find('.placeholder').remove();
+
+ content(this).attr('src', content(this).attr('data-src').replace('w644', 'w1200'));
+
+ content(this).removeAttr('data-src');
+ content(this).removeAttr('data-srcset');
+ });
+
+ content('iframe').each(function () {
+ content(this).attr('src', content(this).attr('data-src'));
+ content(this).removeAttr('data-src');
+ });
+
+ item.description = content('.articleContent').html();
+ item.author = content('meta[property="author"]').attr('content');
+
+ return item;
+ })
+ )
+ );
+
+ ctx.state.data = {
+ title: `${title} - Line Today`,
+ link: currentUrl,
+ item: items,
+ };
+};