.oO SearXNG Developer Documentation Oo.
Loading...
Searching...
No Matches
discourse.py
Go to the documentation of this file.
1# SPDX-License-Identifier: AGPL-3.0-or-later
2""".. sidebar:: info
3
4 - `builtwith.com Discourse <https://trends.builtwith.com/websitelist/Discourse>`_
5
6Discourse is an open source Internet forum system. To search in a forum this
7engine offers some additional settings:
8
9- :py:obj:`base_url`
10- :py:obj:`api_order`
11- :py:obj:`search_endpoint`
12- :py:obj:`show_avatar`
13- :py:obj:`api_key`
14- :py:obj:`api_username`
15
16Example
17=======
18
19To search in your favorite Discourse forum, add a configuration like shown here
20for the ``paddling.com`` forum:
21
22.. code:: yaml
23
24 - name: paddling
25 engine: discourse
26 shortcut: paddle
27 base_url: 'https://forums.paddling.com/'
28 api_order: views
29 categories: ['social media', 'sports']
30 show_avatar: true
31
32If the forum is private, you need to add an API key and username for the search:
33
34.. code:: yaml
35
36 - name: paddling
37 engine: discourse
38 shortcut: paddle
39 base_url: 'https://forums.paddling.com/'
40 api_order: views
41 categories: ['social media', 'sports']
42 show_avatar: true
43 api_key: '<KEY>'
44 api_username: 'system'
45
46
47Implementations
48===============
49
50"""
51
52from urllib.parse import urlencode
53from datetime import datetime, timedelta
54import html
55
56from dateutil import parser
57
58from flask_babel import gettext
59
60about = {
61 "website": "https://discourse.org/",
62 "wikidata_id": "Q15054354",
63 "official_api_documentation": "https://docs.discourse.org/",
64 "use_official_api": True,
65 "require_api_key": False,
66 "results": "JSON",
67}
68
69base_url: str = None # type: ignore
70"""URL of the Discourse forum."""
71
72search_endpoint = '/search.json'
73"""URL path of the `search endpoint`_.
74
75.. _search endpoint: https://docs.discourse.org/#tag/Search
76"""
77
78api_order = 'likes'
79"""Order method, valid values are: ``latest``, ``likes``, ``views``, ``latest_topic``"""
80
81show_avatar = False
82"""Show avatar of the user who send the post."""
83
84api_key = ''
85"""API key of the Discourse forum."""
86
87api_username = ''
88"""API username of the Discourse forum."""
89
90paging = True
91time_range_support = True
92
93AGO_TIMEDELTA = {
94 'day': timedelta(days=1),
95 'week': timedelta(days=7),
96 'month': timedelta(days=31),
97 'year': timedelta(days=365),
98}
99
100
101def request(query, params):
102
103 if len(query) <= 2:
104 return None
105
106 q = [query, f'order:{api_order}']
107 time_range = params.get('time_range')
108 if time_range:
109 after_date = datetime.now() - AGO_TIMEDELTA[time_range]
110 q.append('after:' + after_date.strftime('%Y-%m-%d'))
111
112 args = {
113 'q': ' '.join(q),
114 'page': params['pageno'],
115 }
116
117 params['url'] = f'{base_url}{search_endpoint}?{urlencode(args)}'
118 params['headers'] = {
119 'Accept': 'application/json, text/javascript, */*; q=0.01',
120 'X-Requested-With': 'XMLHttpRequest',
121 }
122
123 if api_key != '':
124 params['headers']['Api-Key'] = api_key
125
126 if api_username != '':
127 params['headers']['Api-Username'] = api_username
128
129 return params
130
131
132def response(resp):
133
134 results = []
135 json_data = resp.json()
136
137 if ('topics' or 'posts') not in json_data.keys():
138 return []
139
140 topics = {}
141
142 for item in json_data['topics']:
143 topics[item['id']] = item
144
145 for post in json_data['posts']:
146 result = topics.get(post['topic_id'], {})
147
148 url = f"{base_url}/p/{post['id']}"
149 status = gettext("closed") if result.get('closed', '') else gettext("open")
150 comments = result.get('posts_count', 0)
151 publishedDate = parser.parse(result['created_at'])
152
153 metadata = []
154 metadata.append('@' + post.get('username', ''))
155
156 if int(comments) > 1:
157 metadata.append(f'{gettext("comments")}: {comments}')
158
159 if result.get('has_accepted_answer'):
160 metadata.append(gettext("answered"))
161 elif int(comments) > 1:
162 metadata.append(status)
163
164 result = {
165 'url': url,
166 'title': html.unescape(result['title']),
167 'content': html.unescape(post.get('blurb', '')),
168 'metadata': ' | '.join(metadata),
169 'publishedDate': publishedDate,
170 'upstream': {'topics': result},
171 }
172
173 avatar = post.get('avatar_template', '').replace('{size}', '96')
174 if show_avatar and avatar:
175 result['thumbnail'] = base_url + avatar
176
177 results.append(result)
178
179 results.append({'number_of_results': len(json_data['topics'])})
180
181 return results
request(query, params)
Definition discourse.py:101