-
Notifications
You must be signed in to change notification settings - Fork 81
/
psiphon_website_info.py
193 lines (156 loc) · 7.04 KB
/
psiphon_website_info.py
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#!/usr/bin/env python
#
# Copyright (c) 2017, Psiphon Inc.
# All rights reserved.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import argparse
import json
import re
import transifex_pull
import urllib.request, urllib.error, urllib.parse
from datetime import datetime
class PsiphonWebsiteInfo:
default_language_code = 'en'
language_code_mappings = {v: k for k, v in transifex_pull.DEFAULT_LANGS.items()} # invert language code mappings
def __init__(self):
self._localization = self.Localization()
class Localization:
def __init__(self):
self.supported_languages = []
self.sync()
def sync(self):
"""Syncs Localization object's state with languages supported programmatically by the Psiphon website"""
resource = urllib.request.urlopen('https://raw.githubusercontent.com/Psiphon-Inc/psiphon-website/master/docpad.coffee')
data = resource.read().decode(resource.headers.get_content_charset())
# Rough regex for declaration of the form:
# languages: ['en', 'fa', 'ar', 'zh', 'bo', '...']
# This is an attempt to detect which languages are enabled on the Psiphon website.
#
# An IndexError will be raised if no matches are found. This will likely occur if
# the target file in source control changes significantly.
# In this scenario failing fast seems desirable.
result = re.search(r'[\s]*languages:.*', data)
line_match = result.group(0)
language_codes = re.search('(\'\w+\',?\s*)+', line_match).group(0).replace(' ', '').replace('\'', '').split(
',')
self.supported_languages = language_codes
def supported(self, code):
return code in self.supported_languages
def languages(self):
return self.supported_languages
class FAQ:
def __init__(self):
self.data = None # stubbed for extension
@staticmethod
def localized_url(code):
return 'https://psiphon.ca/' + code + '/faq.html'
class PrivacyPolicy:
def __init__(self):
self.data = None # stubbed for extension
@staticmethod
def localized_url(code):
return 'https://psiphon.ca/' + code + '/privacy.html'
@property
def localization(self):
return self._localization
def faq_url(self, code):
"""Returns Psiphon faq url for target l10n, falling back on english url"""
if not self._localization.supported(code):
if code in self.language_code_mappings:
code = self.language_code_mappings[code]
else:
code = self.default_language_code
return self.FAQ.localized_url(code)
def privacy_policy_url(self, code):
"""Returns Psiphon privacy policy url for target l10n, falling back on english url"""
if not self._localization.supported(code):
if code in self.language_code_mappings:
code = self.language_code_mappings[code]
else:
code = self.default_language_code
return self.PrivacyPolicy.localized_url(code)
def query_supported_languages_json(website):
"""Returns list of supported language codes in a json string"""
# Example return value is:
# '["en", "fr", "es", ...]'
return json.dumps(website.localization.languages())
def query_supported_languages_with_metadata_json(website, faq=False, privacy_policy=False):
"""Returns mapping of supported language codes to corresponding faq/privacy policy urls in a json string"""
# Example return value is:
# '{
# "en": {
# "faq_url": "https://...",
# "privacy_policy_url": "https://..."
# }
# "fr": {
# ...
# }
# ...
# }'
j = dict()
if faq or privacy_policy:
for code in website.localization.languages():
j[code] = dict()
if faq:
j[code]['faq_url'] = website.faq_url(code)
if privacy_policy:
j[code]['privacy_policy'] = website.privacy_policy_url(code)
return json.dumps(j)
def query_language_support_json(website, code, faq=False, privacy_policy=False):
"""Returns json string which indicates whether the language is supported and what the corresponding faq/privacy
policy urls are"""
# E.g. the following would be returned if queried with code='fr', faq=True, privacy_policy=True:
# '{
# "supported": true,
# "faq_url": "https://psiphon.ca/fr/faq.html",
# "privacy_policy_url": "https://psiphon.ca/fr/privacy.html"
# }'
j = dict()
j['supported'] = website.localization.supported(code)
if faq:
j['faq_url'] = website.faq_url(code)
if privacy_policy:
j['privacy_policy_url'] = website.privacy_policy_url(code)
return json.dumps(j)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
formatter_class=argparse.RawTextHelpFormatter,
prog="psiphon_website_info",
)
parser.add_argument("-l", "--language_code",
help="Query if the given language code is supported on Psiphon's website.",
required=False, type=str)
parser.add_argument("-f", "--faq",
help="Provide corresponding faq page info with query results.",
required=False, action='store_true')
parser.add_argument("-p", "--privacy_policy",
help="Provide corresponding support page info with query results",
required=False, action='store_true')
args = parser.parse_args()
psiphon_website = PsiphonWebsiteInfo()
query_response_json = None
if not args.language_code:
if args.faq or args.privacy_policy:
query_response_json = query_supported_languages_with_metadata_json(psiphon_website,
args.faq,
args.privacy_policy)
else:
query_response_json = query_supported_languages_json(psiphon_website)
else:
query_response_json = query_language_support_json(psiphon_website, args.language_code, args.faq,
args.privacy_policy)
print(query_response_json)
else:
print("[%s] Initialized as a library" % (datetime.now()))