We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Twitter API chokes on at-sign
https://liberapay.com/on/twitter/@korben/ returns a 500. sentry#35, public link: https://sentry.changaco.oy.lc/share/issue/322e3335/.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `liberapay/models/account_elsewhere.py`
Content:
```
1 from __future__ import absolute_import, division, print_function, unicode_literals
2 
3 from datetime import timedelta
4 import json
5 import uuid
6 import xml.etree.ElementTree as ET
7 
8 from six.moves.urllib.parse import urlsplit, urlunsplit
9 
10 from aspen import Response
11 from aspen.utils import utcnow
12 from postgres.orm import Model
13 from psycopg2 import IntegrityError
14 import xmltodict
15 
16 from liberapay.constants import AVATAR_QUERY
17 from liberapay.security.crypto import constant_time_compare
18 from liberapay.website import website
19 
20 
21 CONNECT_TOKEN_TIMEOUT = timedelta(hours=24)
22 
23 
24 class UnknownAccountElsewhere(Exception): pass
25 
26 
27 class AccountElsewhere(Model):
28 
29     typname = "elsewhere_with_participant"
30 
31     def __init__(self, *args, **kwargs):
32         super(AccountElsewhere, self).__init__(*args, **kwargs)
33         self.platform_data = getattr(website.platforms, self.platform)
34 
35 
36     # Constructors
37     # ============
38 
39     @classmethod
40     def from_id(cls, id):
41         """Return an existing AccountElsewhere based on id.
42         """
43         return cls.db.one("""
44             SELECT elsewhere.*::elsewhere_with_participant
45               FROM elsewhere
46              WHERE id = %s
47         """, (id,))
48 
49     @classmethod
50     def from_user_id(cls, platform, user_id):
51         """Return an existing AccountElsewhere based on platform and user_id.
52         """
53         return cls._from_thing('user_id', platform, user_id)
54 
55     @classmethod
56     def from_user_name(cls, platform, user_name):
57         """Return an existing AccountElsewhere based on platform and user_name.
58         """
59         return cls._from_thing('user_name', platform, user_name)
60 
61     @classmethod
62     def _from_thing(cls, thing, platform, value):
63         assert thing in ('user_id', 'user_name')
64         if thing == 'user_name':
65             thing = 'lower(user_name)'
66             value = value.lower()
67         exception = UnknownAccountElsewhere(thing, platform, value)
68         return cls.db.one("""
69 
70             SELECT elsewhere.*::elsewhere_with_participant
71               FROM elsewhere
72              WHERE platform = %s
73                AND {} = %s
74 
75         """.format(thing), (platform, value), default=exception)
76 
77     @classmethod
78     def get_many(cls, platform, user_infos):
79         accounts = []
80         found = cls.db.all("""\
81 
82             SELECT elsewhere.*::elsewhere_with_participant
83               FROM elsewhere
84              WHERE platform = %s
85                AND user_id = any(%s)
86 
87         """, (platform, [i.user_id for i in user_infos]))
88         found = {a.user_id: a for a in found}
89         for i in user_infos:
90             if i.user_id in found:
91                 accounts.append(found[i.user_id])
92             else:
93                 accounts.append(cls.upsert(i))
94         return accounts
95 
96     @classmethod
97     def upsert(cls, i):
98         """Insert or update a user's info.
99         """
100 
101         # Clean up avatar_url
102         if i.avatar_url:
103             scheme, netloc, path, query, fragment = urlsplit(i.avatar_url)
104             fragment = ''
105             if netloc.endswith('githubusercontent.com') or \
106                netloc.endswith('gravatar.com'):
107                 query = AVATAR_QUERY
108             i.avatar_url = urlunsplit((scheme, netloc, path, query, fragment))
109 
110         # Serialize extra_info
111         if isinstance(i.extra_info, ET.Element):
112             i.extra_info = xmltodict.parse(ET.tostring(i.extra_info))
113         i.extra_info = json.dumps(i.extra_info)
114 
115         cols, vals = zip(*i.__dict__.items())
116         cols = ', '.join(cols)
117         placeholders = ', '.join(['%s']*len(vals))
118 
119         try:
120             # Try to insert the account
121             # We do this with a transaction so that if the insert fails, the
122             # participant we reserved for them is rolled back as well.
123             with cls.db.get_cursor() as cursor:
124                 id = cursor.one("""
125                     INSERT INTO participants DEFAULT VALUES RETURNING id
126                 """)
127                 account = cursor.one("""
128                     INSERT INTO elsewhere
129                                 (participant, {0})
130                          VALUES (%s, {1})
131                       RETURNING elsewhere.*::elsewhere_with_participant
132                 """.format(cols, placeholders), (id,)+vals)
133         except IntegrityError:
134             # The account is already in the DB, update it instead
135             account = cls.db.one("""
136                 UPDATE elsewhere
137                    SET ({0}) = ({1})
138                  WHERE platform=%s AND user_id=%s
139              RETURNING elsewhere.*::elsewhere_with_participant
140             """.format(cols, placeholders), vals+(i.platform, i.user_id))
141             if not account:
142                 raise
143 
144         # Return account after propagating avatar_url to participant
145         account.participant.update_avatar()
146         return account
147 
148 
149     # Connect tokens
150     # ==============
151 
152     def check_connect_token(self, token):
153         return (
154             self.connect_token and
155             constant_time_compare(self.connect_token, token) and
156             self.connect_expires > utcnow()
157         )
158 
159     def make_connect_token(self):
160         token = uuid.uuid4().hex
161         expires = utcnow() + CONNECT_TOKEN_TIMEOUT
162         return self.save_connect_token(token, expires)
163 
164     def save_connect_token(self, token, expires):
165         return self.db.one("""
166             UPDATE elsewhere
167                SET connect_token = %s
168                  , connect_expires = %s
169              WHERE id = %s
170          RETURNING connect_token, connect_expires
171         """, (token, expires, self.id))
172 
173 
174     # Random Stuff
175     # ============
176 
177     def get_auth_session(self):
178         if not self.token:
179             return
180         params = dict(token=self.token)
181         if 'refresh_token' in self.token:
182             params['token_updater'] = self.save_token
183         return self.platform_data.get_auth_session(**params)
184 
185     @property
186     def liberapay_slug(self):
187         return self.user_name or ('~' + self.user_id)
188 
189     @property
190     def liberapay_url(self):
191         scheme = website.canonical_scheme
192         host = website.canonical_host
193         platform = self.platform
194         slug = self.liberapay_slug
195         return "{scheme}://{host}/on/{platform}/{slug}/".format(**locals())
196 
197     @property
198     def html_url(self):
199         return self.platform_data.account_url.format(
200             user_id=self.user_id,
201             user_name=self.user_name,
202             platform_data=self.platform_data
203         )
204 
205     @property
206     def friendly_name(self):
207         if getattr(self.platform, 'optional_user_name', False):
208             return self.display_name or self.user_name or self.user_id
209         else:
210             return self.user_name or self.display_name or self.user_id
211 
212     @property
213     def friendly_name_long(self):
214         r = self.friendly_name
215         display_name = self.display_name
216         if display_name and display_name != r:
217             return '%s (%s)' % (r, display_name)
218         user_name = self.user_name
219         if user_name and user_name != r:
220             return '%s (%s)' % (r, user_name)
221         return r
222 
223     def save_token(self, token):
224         """Saves the given access token in the database.
225         """
226         self.db.run("""
227             UPDATE elsewhere
228                SET token = %s
229              WHERE id=%s
230         """, (token, self.id))
231         self.set_attributes(token=token)
232 
233 
234 def get_account_elsewhere(website, state, api_lookup=True):
235     path = state['request'].line.uri.path
236     platform = getattr(website.platforms, path['platform'], None)
237     if platform is None:
238         raise Response(404)
239     uid = path['user_name']
240     if uid[:1] == '~':
241         key = 'user_id'
242         uid = uid[1:]
243     else:
244         key = 'user_name'
245     try:
246         account = AccountElsewhere._from_thing(key, platform.name, uid)
247     except UnknownAccountElsewhere:
248         account = None
249     if not account:
250         if not api_lookup:
251             raise Response(404)
252         try:
253             user_info = platform.get_user_info(key, uid)
254         except Response as r:
255             if r.code == 404:
256                 _ = state['_']
257                 err = _("There doesn't seem to be a user named {0} on {1}.",
258                         uid, platform.display_name)
259                 raise Response(404, err)
260             raise
261         account = AccountElsewhere.upsert(user_info)
262     return platform, account
263
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/liberapay/models/account_elsewhere.py b/liberapay/models/account_elsewhere.py
--- a/liberapay/models/account_elsewhere.py
+++ b/liberapay/models/account_elsewhere.py
@@ -242,6 +242,8 @@
         uid = uid[1:]
     else:
         key = 'user_name'
+        if uid[:1] == '@':
+            uid = uid[1:]
     try:
         account = AccountElsewhere._from_thing(key, platform.name, uid)
     except UnknownAccountElsewhere:

{"golden_diff": "diff --git a/liberapay/models/account_elsewhere.py b/liberapay/models/account_elsewhere.py\n--- a/liberapay/models/account_elsewhere.py\n+++ b/liberapay/models/account_elsewhere.py\n@@ -242,6 +242,8 @@\n         uid = uid[1:]\n     else:\n         key = 'user_name'\n+        if uid[:1] == '@':\n+            uid = uid[1:]\n     try:\n         account = AccountElsewhere._from_thing(key, platform.name, uid)\n     except UnknownAccountElsewhere:\n", "issue": "Twitter API chokes on at-sign\nhttps://liberapay.com/on/twitter/@korben/ returns a 500. sentry#35, public link: https://sentry.changaco.oy.lc/share/issue/322e3335/.\n\n", "before_files": [{"content": "from __future__ import absolute_import, division, print_function, unicode_literals\n\nfrom datetime import timedelta\nimport json\nimport uuid\nimport xml.etree.ElementTree as ET\n\nfrom six.moves.urllib.parse import urlsplit, urlunsplit\n\nfrom aspen import Response\nfrom aspen.utils import utcnow\nfrom postgres.orm import Model\nfrom psycopg2 import IntegrityError\nimport xmltodict\n\nfrom liberapay.constants import AVATAR_QUERY\nfrom liberapay.security.crypto import constant_time_compare\nfrom liberapay.website import website\n\n\nCONNECT_TOKEN_TIMEOUT = timedelta(hours=24)\n\n\nclass UnknownAccountElsewhere(Exception): pass\n\n\nclass AccountElsewhere(Model):\n\n    typname = \"elsewhere_with_participant\"\n\n    def __init__(self, *args, **kwargs):\n        super(AccountElsewhere, self).__init__(*args, **kwargs)\n        self.platform_data = getattr(website.platforms, self.platform)\n\n\n    # Constructors\n    # ============\n\n    @classmethod\n    def from_id(cls, id):\n        \"\"\"Return an existing AccountElsewhere based on id.\n        \"\"\"\n        return cls.db.one(\"\"\"\n            SELECT elsewhere.*::elsewhere_with_participant\n              FROM elsewhere\n             WHERE id = %s\n        \"\"\", (id,))\n\n    @classmethod\n    def from_user_id(cls, platform, user_id):\n        \"\"\"Return an existing AccountElsewhere based on platform and user_id.\n        \"\"\"\n        return cls._from_thing('user_id', platform, user_id)\n\n    @classmethod\n    def from_user_name(cls, platform, user_name):\n        \"\"\"Return an existing AccountElsewhere based on platform and user_name.\n        \"\"\"\n        return cls._from_thing('user_name', platform, user_name)\n\n    @classmethod\n    def _from_thing(cls, thing, platform, value):\n        assert thing in ('user_id', 'user_name')\n        if thing == 'user_name':\n            thing = 'lower(user_name)'\n            value = value.lower()\n        exception = UnknownAccountElsewhere(thing, platform, value)\n        return cls.db.one(\"\"\"\n\n            SELECT elsewhere.*::elsewhere_with_participant\n              FROM elsewhere\n             WHERE platform = %s\n               AND {} = %s\n\n        \"\"\".format(thing), (platform, value), default=exception)\n\n    @classmethod\n    def get_many(cls, platform, user_infos):\n        accounts = []\n        found = cls.db.all(\"\"\"\\\n\n            SELECT elsewhere.*::elsewhere_with_participant\n              FROM elsewhere\n             WHERE platform = %s\n               AND user_id = any(%s)\n\n        \"\"\", (platform, [i.user_id for i in user_infos]))\n        found = {a.user_id: a for a in found}\n        for i in user_infos:\n            if i.user_id in found:\n                accounts.append(found[i.user_id])\n            else:\n                accounts.append(cls.upsert(i))\n        return accounts\n\n    @classmethod\n    def upsert(cls, i):\n        \"\"\"Insert or update a user's info.\n        \"\"\"\n\n        # Clean up avatar_url\n        if i.avatar_url:\n            scheme, netloc, path, query, fragment = urlsplit(i.avatar_url)\n            fragment = ''\n            if netloc.endswith('githubusercontent.com') or \\\n               netloc.endswith('gravatar.com'):\n                query = AVATAR_QUERY\n            i.avatar_url = urlunsplit((scheme, netloc, path, query, fragment))\n\n        # Serialize extra_info\n        if isinstance(i.extra_info, ET.Element):\n            i.extra_info = xmltodict.parse(ET.tostring(i.extra_info))\n        i.extra_info = json.dumps(i.extra_info)\n\n        cols, vals = zip(*i.__dict__.items())\n        cols = ', '.join(cols)\n        placeholders = ', '.join(['%s']*len(vals))\n\n        try:\n            # Try to insert the account\n            # We do this with a transaction so that if the insert fails, the\n            # participant we reserved for them is rolled back as well.\n            with cls.db.get_cursor() as cursor:\n                id = cursor.one(\"\"\"\n                    INSERT INTO participants DEFAULT VALUES RETURNING id\n                \"\"\")\n                account = cursor.one(\"\"\"\n                    INSERT INTO elsewhere\n                                (participant, {0})\n                         VALUES (%s, {1})\n                      RETURNING elsewhere.*::elsewhere_with_participant\n                \"\"\".format(cols, placeholders), (id,)+vals)\n        except IntegrityError:\n            # The account is already in the DB, update it instead\n            account = cls.db.one(\"\"\"\n                UPDATE elsewhere\n                   SET ({0}) = ({1})\n                 WHERE platform=%s AND user_id=%s\n             RETURNING elsewhere.*::elsewhere_with_participant\n            \"\"\".format(cols, placeholders), vals+(i.platform, i.user_id))\n            if not account:\n                raise\n\n        # Return account after propagating avatar_url to participant\n        account.participant.update_avatar()\n        return account\n\n\n    # Connect tokens\n    # ==============\n\n    def check_connect_token(self, token):\n        return (\n            self.connect_token and\n            constant_time_compare(self.connect_token, token) and\n            self.connect_expires > utcnow()\n        )\n\n    def make_connect_token(self):\n        token = uuid.uuid4().hex\n        expires = utcnow() + CONNECT_TOKEN_TIMEOUT\n        return self.save_connect_token(token, expires)\n\n    def save_connect_token(self, token, expires):\n        return self.db.one(\"\"\"\n            UPDATE elsewhere\n               SET connect_token = %s\n                 , connect_expires = %s\n             WHERE id = %s\n         RETURNING connect_token, connect_expires\n        \"\"\", (token, expires, self.id))\n\n\n    # Random Stuff\n    # ============\n\n    def get_auth_session(self):\n        if not self.token:\n            return\n        params = dict(token=self.token)\n        if 'refresh_token' in self.token:\n            params['token_updater'] = self.save_token\n        return self.platform_data.get_auth_session(**params)\n\n    @property\n    def liberapay_slug(self):\n        return self.user_name or ('~' + self.user_id)\n\n    @property\n    def liberapay_url(self):\n        scheme = website.canonical_scheme\n        host = website.canonical_host\n        platform = self.platform\n        slug = self.liberapay_slug\n        return \"{scheme}://{host}/on/{platform}/{slug}/\".format(**locals())\n\n    @property\n    def html_url(self):\n        return self.platform_data.account_url.format(\n            user_id=self.user_id,\n            user_name=self.user_name,\n            platform_data=self.platform_data\n        )\n\n    @property\n    def friendly_name(self):\n        if getattr(self.platform, 'optional_user_name', False):\n            return self.display_name or self.user_name or self.user_id\n        else:\n            return self.user_name or self.display_name or self.user_id\n\n    @property\n    def friendly_name_long(self):\n        r = self.friendly_name\n        display_name = self.display_name\n        if display_name and display_name != r:\n            return '%s (%s)' % (r, display_name)\n        user_name = self.user_name\n        if user_name and user_name != r:\n            return '%s (%s)' % (r, user_name)\n        return r\n\n    def save_token(self, token):\n        \"\"\"Saves the given access token in the database.\n        \"\"\"\n        self.db.run(\"\"\"\n            UPDATE elsewhere\n               SET token = %s\n             WHERE id=%s\n        \"\"\", (token, self.id))\n        self.set_attributes(token=token)\n\n\ndef get_account_elsewhere(website, state, api_lookup=True):\n    path = state['request'].line.uri.path\n    platform = getattr(website.platforms, path['platform'], None)\n    if platform is None:\n        raise Response(404)\n    uid = path['user_name']\n    if uid[:1] == '~':\n        key = 'user_id'\n        uid = uid[1:]\n    else:\n        key = 'user_name'\n    try:\n        account = AccountElsewhere._from_thing(key, platform.name, uid)\n    except UnknownAccountElsewhere:\n        account = None\n    if not account:\n        if not api_lookup:\n            raise Response(404)\n        try:\n            user_info = platform.get_user_info(key, uid)\n        except Response as r:\n            if r.code == 404:\n                _ = state['_']\n                err = _(\"There doesn't seem to be a user named {0} on {1}.\",\n                        uid, platform.display_name)\n                raise Response(404, err)\n            raise\n        account = AccountElsewhere.upsert(user_info)\n    return platform, account\n", "path": "liberapay/models/account_elsewhere.py"}], "after_files": [{"content": "from __future__ import absolute_import, division, print_function, unicode_literals\n\nfrom datetime import timedelta\nimport json\nimport uuid\nimport xml.etree.ElementTree as ET\n\nfrom six.moves.urllib.parse import urlsplit, urlunsplit\n\nfrom aspen import Response\nfrom aspen.utils import utcnow\nfrom postgres.orm import Model\nfrom psycopg2 import IntegrityError\nimport xmltodict\n\nfrom liberapay.constants import AVATAR_QUERY\nfrom liberapay.security.crypto import constant_time_compare\nfrom liberapay.website import website\n\n\nCONNECT_TOKEN_TIMEOUT = timedelta(hours=24)\n\n\nclass UnknownAccountElsewhere(Exception): pass\n\n\nclass AccountElsewhere(Model):\n\n    typname = \"elsewhere_with_participant\"\n\n    def __init__(self, *args, **kwargs):\n        super(AccountElsewhere, self).__init__(*args, **kwargs)\n        self.platform_data = getattr(website.platforms, self.platform)\n\n\n    # Constructors\n    # ============\n\n    @classmethod\n    def from_id(cls, id):\n        \"\"\"Return an existing AccountElsewhere based on id.\n        \"\"\"\n        return cls.db.one(\"\"\"\n            SELECT elsewhere.*::elsewhere_with_participant\n              FROM elsewhere\n             WHERE id = %s\n        \"\"\", (id,))\n\n    @classmethod\n    def from_user_id(cls, platform, user_id):\n        \"\"\"Return an existing AccountElsewhere based on platform and user_id.\n        \"\"\"\n        return cls._from_thing('user_id', platform, user_id)\n\n    @classmethod\n    def from_user_name(cls, platform, user_name):\n        \"\"\"Return an existing AccountElsewhere based on platform and user_name.\n        \"\"\"\n        return cls._from_thing('user_name', platform, user_name)\n\n    @classmethod\n    def _from_thing(cls, thing, platform, value):\n        assert thing in ('user_id', 'user_name')\n        if thing == 'user_name':\n            thing = 'lower(user_name)'\n            value = value.lower()\n        exception = UnknownAccountElsewhere(thing, platform, value)\n        return cls.db.one(\"\"\"\n\n            SELECT elsewhere.*::elsewhere_with_participant\n              FROM elsewhere\n             WHERE platform = %s\n               AND {} = %s\n\n        \"\"\".format(thing), (platform, value), default=exception)\n\n    @classmethod\n    def get_many(cls, platform, user_infos):\n        accounts = []\n        found = cls.db.all(\"\"\"\\\n\n            SELECT elsewhere.*::elsewhere_with_participant\n              FROM elsewhere\n             WHERE platform = %s\n               AND user_id = any(%s)\n\n        \"\"\", (platform, [i.user_id for i in user_infos]))\n        found = {a.user_id: a for a in found}\n        for i in user_infos:\n            if i.user_id in found:\n                accounts.append(found[i.user_id])\n            else:\n                accounts.append(cls.upsert(i))\n        return accounts\n\n    @classmethod\n    def upsert(cls, i):\n        \"\"\"Insert or update a user's info.\n        \"\"\"\n\n        # Clean up avatar_url\n        if i.avatar_url:\n            scheme, netloc, path, query, fragment = urlsplit(i.avatar_url)\n            fragment = ''\n            if netloc.endswith('githubusercontent.com') or \\\n               netloc.endswith('gravatar.com'):\n                query = AVATAR_QUERY\n            i.avatar_url = urlunsplit((scheme, netloc, path, query, fragment))\n\n        # Serialize extra_info\n        if isinstance(i.extra_info, ET.Element):\n            i.extra_info = xmltodict.parse(ET.tostring(i.extra_info))\n        i.extra_info = json.dumps(i.extra_info)\n\n        cols, vals = zip(*i.__dict__.items())\n        cols = ', '.join(cols)\n        placeholders = ', '.join(['%s']*len(vals))\n\n        try:\n            # Try to insert the account\n            # We do this with a transaction so that if the insert fails, the\n            # participant we reserved for them is rolled back as well.\n            with cls.db.get_cursor() as cursor:\n                id = cursor.one(\"\"\"\n                    INSERT INTO participants DEFAULT VALUES RETURNING id\n                \"\"\")\n                account = cursor.one(\"\"\"\n                    INSERT INTO elsewhere\n                                (participant, {0})\n                         VALUES (%s, {1})\n                      RETURNING elsewhere.*::elsewhere_with_participant\n                \"\"\".format(cols, placeholders), (id,)+vals)\n        except IntegrityError:\n            # The account is already in the DB, update it instead\n            account = cls.db.one(\"\"\"\n                UPDATE elsewhere\n                   SET ({0}) = ({1})\n                 WHERE platform=%s AND user_id=%s\n             RETURNING elsewhere.*::elsewhere_with_participant\n            \"\"\".format(cols, placeholders), vals+(i.platform, i.user_id))\n            if not account:\n                raise\n\n        # Return account after propagating avatar_url to participant\n        account.participant.update_avatar()\n        return account\n\n\n    # Connect tokens\n    # ==============\n\n    def check_connect_token(self, token):\n        return (\n            self.connect_token and\n            constant_time_compare(self.connect_token, token) and\n            self.connect_expires > utcnow()\n        )\n\n    def make_connect_token(self):\n        token = uuid.uuid4().hex\n        expires = utcnow() + CONNECT_TOKEN_TIMEOUT\n        return self.save_connect_token(token, expires)\n\n    def save_connect_token(self, token, expires):\n        return self.db.one(\"\"\"\n            UPDATE elsewhere\n               SET connect_token = %s\n                 , connect_expires = %s\n             WHERE id = %s\n         RETURNING connect_token, connect_expires\n        \"\"\", (token, expires, self.id))\n\n\n    # Random Stuff\n    # ============\n\n    def get_auth_session(self):\n        if not self.token:\n            return\n        params = dict(token=self.token)\n        if 'refresh_token' in self.token:\n            params['token_updater'] = self.save_token\n        return self.platform_data.get_auth_session(**params)\n\n    @property\n    def liberapay_slug(self):\n        return self.user_name or ('~' + self.user_id)\n\n    @property\n    def liberapay_url(self):\n        scheme = website.canonical_scheme\n        host = website.canonical_host\n        platform = self.platform\n        slug = self.liberapay_slug\n        return \"{scheme}://{host}/on/{platform}/{slug}/\".format(**locals())\n\n    @property\n    def html_url(self):\n        return self.platform_data.account_url.format(\n            user_id=self.user_id,\n            user_name=self.user_name,\n            platform_data=self.platform_data\n        )\n\n    @property\n    def friendly_name(self):\n        if getattr(self.platform, 'optional_user_name', False):\n            return self.display_name or self.user_name or self.user_id\n        else:\n            return self.user_name or self.display_name or self.user_id\n\n    @property\n    def friendly_name_long(self):\n        r = self.friendly_name\n        display_name = self.display_name\n        if display_name and display_name != r:\n            return '%s (%s)' % (r, display_name)\n        user_name = self.user_name\n        if user_name and user_name != r:\n            return '%s (%s)' % (r, user_name)\n        return r\n\n    def save_token(self, token):\n        \"\"\"Saves the given access token in the database.\n        \"\"\"\n        self.db.run(\"\"\"\n            UPDATE elsewhere\n               SET token = %s\n             WHERE id=%s\n        \"\"\", (token, self.id))\n        self.set_attributes(token=token)\n\n\ndef get_account_elsewhere(website, state, api_lookup=True):\n    path = state['request'].line.uri.path\n    platform = getattr(website.platforms, path['platform'], None)\n    if platform is None:\n        raise Response(404)\n    uid = path['user_name']\n    if uid[:1] == '~':\n        key = 'user_id'\n        uid = uid[1:]\n    else:\n        key = 'user_name'\n        if uid[:1] == '@':\n            uid = uid[1:]\n    try:\n        account = AccountElsewhere._from_thing(key, platform.name, uid)\n    except UnknownAccountElsewhere:\n        account = None\n    if not account:\n        if not api_lookup:\n            raise Response(404)\n        try:\n            user_info = platform.get_user_info(key, uid)\n        except Response as r:\n            if r.code == 404:\n                _ = state['_']\n                err = _(\"There doesn't seem to be a user named {0} on {1}.\",\n                        uid, platform.display_name)\n                raise Response(404, err)\n            raise\n        account = AccountElsewhere.upsert(user_info)\n    return platform, account\n", "path": "liberapay/models/account_elsewhere.py"}]}