-
Notifications
You must be signed in to change notification settings - Fork 0
/
mentions-united-provider_pixelfed.js
167 lines (146 loc) · 6.32 KB
/
mentions-united-provider_pixelfed.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/**
* Mentions United Provider plugin class for retreiving interactions from Pixelfed
*
* @author Kristof Zerbe
* @version 2.0.0
* @see {@link https://github.com/kristofzerbe/MentionsUnited|GitHub}
*
* API Documentation: There is no proper API documentation, but the source code is freely available at:
* https://github.com/pixelfed/pixelfed/blob/dev/app/Http/Controllers/Api/ApiV1Controller.php
*
* Options:
* - {String} syndicationUrl = Full URL of the Pixelfed post
* - {String} [syndicationTitle] = Title of the Pixelfed post, if multiple syndications of original post
* - {String} [apiBaseUrl] = Base URL of API proxy, if existing
* - {String} [apiTokenReadOnly] = Token to access Pixelfed's API in Read-Only mode, if no proxy
*
* Supported origins:
* - pixelfed
*
* Supported type-verbs:
* - repost
* - like
* - reply
*
* Remarks:
* - This implementation relies either on a READ-ONLY token of your Pixelfed instance,
* then you have to set the option 'apiBaseUrl' in the calling code to the URL of
* your instance, or you use an API PROXY which forwards the requests to the
* Pixelfed API according to the same URL scheme. In this case the URL of your
* proxy has to be set to 'apiBaseUrl' and you can leave 'apiTokenReadOnly' blank.
*
* IMPORTANT: The API Token approach means, that your token is visible in your
* JavaScript code and is therefore PUBLICLY AVAILABLE and could be used by ANYONE!
* You can search for ‘tokenCan(’read‘)’ in the Pixelfed source code to see what
* information you are making freely available with it.
* However, both variants require the token, that you can generate your token at:
* https://__INSTANCE__/settings/applications.
*
*/
class MentionsUnitedProvider_Pixelfed extends MentionsUnited.Provider {
key = ""; // will be set via syndicationUrl in constructor (must be unique across all provider plugins for registration)
options = {
syndicationUrl: "",
syndicationTitle: "",
apiBaseUrl: "",
apiTokenReadOnly: ""
}
constructor(options) {
super();
this.options = {...this.options, ...options};
//check mandatory options
if (this.options.syndicationUrl.length === 0) { throw "'syndicationUrl' is missing"; }
if (this.options.apiBaseUrl.length === 0 && this.options.apiTokenReadOnly.length === 0) {
throw "'apiTokenReadOnly' is missing, as no 'apiBaseUrl' is defined";
}
//get needed information from syndicationUrl
let pixelfedUrl = new URL(this.options.syndicationUrl);
this.key = pixelfedUrl.hostname;
this.sourceId = pixelfedUrl.pathname.split("/").pop();
this.options.apiBaseUrl = this.options.apiBaseUrl ?? pixelfedUrl.origin;
}
/**
* Retrieve data from Pixelfed
* @returns {Array}
*/
async retrieve() {
const msg = `${this.constructor.name}: Retreiving interactions for '${this.options.syndicationUrl}'`;
console.time(msg);
// 1 - Reposts
let interactionsReblogged = [];
try {
const apiResponseReblogged = await fetch(this.rebloggedApiUrl(), {
headers: { Authorization: `Bearer ${this.options.apiTokenReadOnly}` }
});
const apiDataReblogged = await apiResponseReblogged.json();
interactionsReblogged = this.#processJsonData(apiDataReblogged ?? [], "repost");
} catch (e) { console.error(e); }
// 2 - Likes
let interactionsFavorited = [];
try {
const apiResponseFavorited = await fetch(this.favoritedApiUrl(), {
headers: { Authorization: `Bearer ${this.options.apiTokenReadOnly}` }
});
const apiDataFavorited = await apiResponseFavorited.json();
interactionsFavorited = this.#processJsonData(apiDataFavorited ?? [], "like");
} catch (e) { console.error(e); }
// 3 - Replies
let interactionsContext = [];
try {
const apiResponseContext = await fetch(this.contextApiUrl(), {
headers: { Authorization: `Bearer ${this.options.apiTokenReadOnly}` }
});
const apiDataContext = await apiResponseContext.json();
interactionsContext = this.#processJsonData(apiDataContext.descendants ?? [], "reply");
} catch (e) { console.error(e); }
console.timeEnd(msg);
return [...interactionsReblogged, ...interactionsFavorited, ... interactionsContext];
}
//////////////////////////////////////////////////////////////////////////////////////////
statusApiUrl() { return `${this.options.apiBaseUrl}/api/v1/statuses/${this.sourceId}` };
contextApiUrl() { return `${this.options.apiBaseUrl}/api/v1/statuses/${this.sourceId}/context` };
favoritedApiUrl() { return `${this.options.apiBaseUrl}/api/v1/statuses/${this.sourceId}/favourited_by`; }
rebloggedApiUrl() { return `${this.options.apiBaseUrl}/api/v1/statuses/${this.sourceId}/reblogged_by`; }
/**
* Processes retrieved JSON data into flat array of Interactions
* @param {Array.<Object>} entries
* @param {String} type
* @returns {Array.<MentionsUnited.Interaction>}
*/
#processJsonData(entries, type) {
return entries.map((item) => {
return this.#convertToInteraction(item, type);
});
}
/**
* Converts specific data object into Interaction
* @param {Object} entry
* @returns {MentionsUnited.Interaction}
*/
#convertToInteraction(entry, type) {
let r = new MentionsUnited.Interaction();
r.syndication.url = this.options.syndicationUrl;
r.syndication.title = this.options.syndicationTitle;
r.type = type;
r.received = (type === "reply") ? entry.created_at : undefined;
r.source.provider = this.key;
r.source.origin = "pixelfed";
r.source.sender = this.key;
r.source.url = this.options.syndicationUrl;
r.source.id = entry.id;
r.source.title = ""; //TODO: REMOVE: this.options.syndicationTitle;
r.author.name = (type === "reply") ? entry.account.display_name : entry.display_name;
r.author.avatar = (type === "reply") ? entry.account.avatar : entry.avatar;
r.author.profile = (type === "reply") ? entry.account.url : entry.url;
r.content.text = entry.content;
return r;
}
}
/**
* Changelog
*
* 1.0.0 - Initial
* 1.0.1 - Introduction of 'sourceTitle', to be able to distinguish several Pixelfed sources textually
* 2.0.0 - Changed option names due to risk of confusion
* - Introducting interaction.syndication
*/