| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 | # SPDX-License-Identifier: AGPL-3.0-or-later""".. sidebar:: info  - `builtwith.com Discourse <https://trends.builtwith.com/websitelist/Discourse>`_Discourse is an open source Internet forum system.  To search in a forum thisengine offers some additional settings:- :py:obj:`base_url`- :py:obj:`api_order`- :py:obj:`search_endpoint`- :py:obj:`show_avatar`- :py:obj:`api_key`- :py:obj:`api_username`Example=======To search in your favorite Discourse forum, add a configuration like shown herefor the ``paddling.com`` forum:.. code:: yaml   - name: paddling     engine: discourse     shortcut: paddle     base_url: 'https://forums.paddling.com/'     api_order: views     categories: ['social media', 'sports']     show_avatar: trueIf the forum is private, you need to add an API key and username for the search:.. code:: yaml   - name: paddling     engine: discourse     shortcut: paddle     base_url: 'https://forums.paddling.com/'     api_order: views     categories: ['social media', 'sports']     show_avatar: true     api_key: '<KEY>'     api_username: 'system'Implementations==============="""from urllib.parse import urlencodefrom datetime import datetime, timedeltaimport htmlfrom dateutil import parserfrom flask_babel import gettextabout = {    "website": "https://discourse.org/",    "wikidata_id": "Q15054354",    "official_api_documentation": "https://docs.discourse.org/",    "use_official_api": True,    "require_api_key": False,    "results": "JSON",}base_url: str = None  # type: ignore"""URL of the Discourse forum."""search_endpoint = '/search.json'"""URL path of the `search endpoint`_... _search endpoint: https://docs.discourse.org/#tag/Search"""api_order = 'likes'"""Order method, valid values are: ``latest``, ``likes``, ``views``, ``latest_topic``"""show_avatar = False"""Show avatar of the user who send the post."""api_key = ''"""API key of the Discourse forum."""api_username = ''"""API username of the Discourse forum."""paging = Truetime_range_support = TrueAGO_TIMEDELTA = {    'day': timedelta(days=1),    'week': timedelta(days=7),    'month': timedelta(days=31),    'year': timedelta(days=365),}def request(query, params):    if len(query) <= 2:        return None    q = [query, f'order:{api_order}']    time_range = params.get('time_range')    if time_range:        after_date = datetime.now() - AGO_TIMEDELTA[time_range]        q.append('after:' + after_date.strftime('%Y-%m-%d'))    args = {        'q': ' '.join(q),        'page': params['pageno'],    }    params['url'] = f'{base_url}{search_endpoint}?{urlencode(args)}'    params['headers'] = {        'Accept': 'application/json, text/javascript, */*; q=0.01',        'X-Requested-With': 'XMLHttpRequest',    }    if api_key != '':        params['headers']['Api-Key'] = api_key    if api_username != '':        params['headers']['Api-Username'] = api_username    return paramsdef response(resp):    results = []    json_data = resp.json()    if ('topics' or 'posts') not in json_data.keys():        return []    topics = {}    for item in json_data['topics']:        topics[item['id']] = item    for post in json_data['posts']:        result = topics.get(post['topic_id'], {})        url = f"{base_url}/p/{post['id']}"        status = gettext("closed") if result.get('closed', '') else gettext("open")        comments = result.get('posts_count', 0)        publishedDate = parser.parse(result['created_at'])        metadata = []        metadata.append('@' + post.get('username', ''))        if int(comments) > 1:            metadata.append(f'{gettext("comments")}: {comments}')        if result.get('has_accepted_answer'):            metadata.append(gettext("answered"))        elif int(comments) > 1:            metadata.append(status)        result = {            'url': url,            'title': html.unescape(result['title']),            'content': html.unescape(post.get('blurb', '')),            'metadata': ' | '.join(metadata),            'publishedDate': publishedDate,            'upstream': {'topics': result},        }        avatar = post.get('avatar_template', '').replace('{size}', '96')        if show_avatar and avatar:            result['thumbnail'] = base_url + avatar        results.append(result)    results.append({'number_of_results': len(json_data['topics'])})    return results
 |