diff --git a/docs/en/social-media.md b/docs/en/social-media.md
index fe0ecf5166..7e33349a79 100644
--- a/docs/en/social-media.md
+++ b/docs/en/social-media.md
@@ -4,6 +4,24 @@ pageClass: routes
# Social Media
+## Crossbell
+
+### Notes
+
+
+
+### Notes of character
+
+
+
+### Notes of source
+
+
+
+### Feeds of following
+
+
+
## CuriousCat
### User
diff --git a/docs/social-media.md b/docs/social-media.md
index 34d44f97ac..6279d19f5c 100644
--- a/docs/social-media.md
+++ b/docs/social-media.md
@@ -344,6 +344,24 @@ Tiny Tiny RSS 会给所有 iframe 元素添加 `sandbox="allow-scripts"` 属性
+## Crossbell
+
+### Notes
+
+
+
+### Notes of character
+
+
+
+### Notes of source
+
+
+
+### Feeds of following
+
+
+
## Curius
### 用户
diff --git a/lib/v2/crossbell/feeds/following.js b/lib/v2/crossbell/feeds/following.js
new file mode 100644
index 0000000000..65cea84d46
--- /dev/null
+++ b/lib/v2/crossbell/feeds/following.js
@@ -0,0 +1,22 @@
+const got = require('@/utils/got');
+
+module.exports = async (ctx) => {
+ const characterId = ctx.params.characterId;
+ const response = await got(`https://indexer.crossbell.io/v1/characters/${characterId}/feed/follow`);
+ ctx.state.data = {
+ title: 'Crossbell Feeds of ' + characterId,
+ link: 'https://crossbell.io/',
+ item: response.data?.list?.map((item) => ({
+ title: `${item.type} ${item.character.metadata?.content?.name}@${item.character.handle}`,
+ description: `${item.type} ${item.note && `
Note: ${item.note.metadata?.content?.title || item.note.metadata?.content?.content}`}${
+ item.character && `
Character: ${item.character.metadata?.content?.name}@${item.character.handle}`
+ }`,
+ link: item.note ? item.note?.metadata?.content?.external_urls || `https://crossbell.io/notes/${item.note?.characterId}-${item.note?.noteId}` : 'https://xchar.app/' + item.character?.handle,
+ pubDate: item.createdAt,
+ updated: item.updatedAt,
+ author: item.note?.metadata?.content?.authors?.[0] || item.note?.character?.metadata?.content?.name || item.note?.character?.handle || item.owner,
+ guid: item.transactionHash + item.logIndex + item.type,
+ category: [...(item.note?.metadata?.content?.sources || []), ...(item.note?.metadata?.content?.tags || [])],
+ })),
+ };
+};
diff --git a/lib/v2/crossbell/maintainer.js b/lib/v2/crossbell/maintainer.js
new file mode 100644
index 0000000000..1414ea3b0b
--- /dev/null
+++ b/lib/v2/crossbell/maintainer.js
@@ -0,0 +1,6 @@
+module.exports = {
+ '/notes': ['DIYgod'],
+ '/notes/character/:characterId': ['DIYgod'],
+ '/notes/source/:source': ['DIYgod'],
+ '/feeds/following/:characterId': ['DIYgod'],
+};
diff --git a/lib/v2/crossbell/notes/character.js b/lib/v2/crossbell/notes/character.js
new file mode 100644
index 0000000000..c688feb1a2
--- /dev/null
+++ b/lib/v2/crossbell/notes/character.js
@@ -0,0 +1,22 @@
+const got = require('@/utils/got');
+const { getItem } = require('./utils');
+
+module.exports = async (ctx) => {
+ const characterId = ctx.params.characterId;
+
+ const response = await got('https://indexer.crossbell.io/v1/notes', {
+ searchParams: {
+ characterId,
+ includeCharacter: true,
+ },
+ });
+
+ const name = response.data?.list?.[0]?.character?.metadata?.content?.name || response.data?.list?.[0]?.character?.handle || characterId;
+ const handle = response.data?.list?.[0]?.character?.handle;
+
+ ctx.state.data = {
+ title: 'Crossbell Notes from ' + name,
+ link: 'https://xchar.app/' + handle,
+ item: response.data?.list?.map((item) => getItem(item)),
+ };
+};
diff --git a/lib/v2/crossbell/notes/index.js b/lib/v2/crossbell/notes/index.js
new file mode 100644
index 0000000000..1450ddc4b8
--- /dev/null
+++ b/lib/v2/crossbell/notes/index.js
@@ -0,0 +1,16 @@
+const got = require('@/utils/got');
+const { getItem } = require('./utils');
+
+module.exports = async (ctx) => {
+ const response = await got('https://indexer.crossbell.io/v1/notes', {
+ searchParams: {
+ includeCharacter: true,
+ },
+ });
+
+ ctx.state.data = {
+ title: 'Crossbell Notes',
+ link: 'https://crossbell.io/',
+ item: response.data?.list?.map((item) => getItem(item)),
+ };
+};
diff --git a/lib/v2/crossbell/notes/source.js b/lib/v2/crossbell/notes/source.js
new file mode 100644
index 0000000000..53edab3c47
--- /dev/null
+++ b/lib/v2/crossbell/notes/source.js
@@ -0,0 +1,19 @@
+const got = require('@/utils/got');
+const { getItem } = require('./utils');
+
+module.exports = async (ctx) => {
+ const source = ctx.params.source;
+
+ const response = await got('https://indexer.crossbell.io/v1/notes', {
+ searchParams: {
+ sources: source,
+ includeCharacter: true,
+ },
+ });
+
+ ctx.state.data = {
+ title: 'Crossbell Notes from ' + source,
+ link: 'https://crossbell.io/',
+ item: response.data?.list?.map((item) => getItem(item)),
+ };
+};
diff --git a/lib/v2/crossbell/notes/utils.js b/lib/v2/crossbell/notes/utils.js
new file mode 100644
index 0000000000..a1f45e9eab
--- /dev/null
+++ b/lib/v2/crossbell/notes/utils.js
@@ -0,0 +1,12 @@
+module.exports = {
+ getItem: (note) => ({
+ title: note.metadata?.content?.title || '',
+ description: note.metadata?.content?.content,
+ link: note.metadata?.content?.external_urls || `https://crossbell.io/notes/${note.characterId}-${note.noteId}`,
+ pubDate: note.metadata?.content?.publishedAt,
+ updated: note.metadata?.content?.updatedAt,
+ author: note.metadata?.content?.authors?.[0] || note.character?.metadata?.content?.name || note.character?.handle,
+ guid: `${note.characterId}-${note.noteId}`,
+ category: [...(note.metadata?.content?.sources || []), ...(note.metadata?.content?.tags || [])],
+ }),
+};
diff --git a/lib/v2/crossbell/radar.js b/lib/v2/crossbell/radar.js
new file mode 100644
index 0000000000..1052da1fb8
--- /dev/null
+++ b/lib/v2/crossbell/radar.js
@@ -0,0 +1,24 @@
+module.exports = {
+ 'crossbell.io': {
+ _name: 'Crossbell',
+ '.': [
+ {
+ title: 'Notes',
+ docs: 'https://docs.rsshub.app/social-media.html#crossbell',
+ source: '/*',
+ target: '/notes',
+ },
+ ],
+ },
+ 'xlog.app': {
+ _name: 'xLog',
+ '.': [
+ {
+ title: 'Notes',
+ docs: 'https://docs.rsshub.app/social-media.html#crossbell',
+ source: '/*',
+ target: '/notes/source/xlog',
+ },
+ ],
+ },
+};
diff --git a/lib/v2/crossbell/router.js b/lib/v2/crossbell/router.js
new file mode 100644
index 0000000000..1558bc6253
--- /dev/null
+++ b/lib/v2/crossbell/router.js
@@ -0,0 +1,6 @@
+module.exports = function (router) {
+ router.get('/notes/character/:characterId', require('./notes/character'));
+ router.get('/notes/source/:source', require('./notes/source'));
+ router.get('/notes', require('./notes/index'));
+ router.get('/feeds/following/:characterId', require('./feeds/following'));
+};