Add transifex

This commit is contained in:
Mohammad Osama Khan 2022-12-14 13:03:48 +05:30
parent b4be69017b
commit 03132afaf7
No known key found for this signature in database
GPG Key ID: 15F978BEFADAB9E1
17 changed files with 804 additions and 0 deletions

9
.tx/config Normal file
View File

@ -0,0 +1,9 @@
[main]
host = https://www.transifex.com
api_token = ''
[pybitmessage-test.messages-pot]
file_filter = <lang>po
minimum_perc = 0
source_file = messages.pot
source_lang = en
type = PO

5
Dockerfile Normal file
View File

@ -0,0 +1,5 @@
FROM python:3
ADD /app/requirements.txt /
RUN pip install -r requirements.txt
ADD /app/transifex_demo.py /
CMD [ "python", "transifex_demo.py" ]

400
app/messages.pot Normal file
View File

@ -0,0 +1,400 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-12-20 19:29+0530\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: main.kv:107
msgid "Accounts"
msgstr ""
#: main.kv:126
msgid "Inbox"
msgstr ""
#: main.kv:134
msgid "Sent"
msgstr ""
#: main.kv:141
msgid "Draft"
msgstr ""
#: main.kv:148
msgid "Trash"
msgstr ""
#: main.kv:156
msgid "All Mails"
msgstr ""
#: main.kv:164
msgid "All labels"
msgstr ""
#: main.kv:166
msgid "Address Book"
msgstr ""
#: main.kv:172
msgid "Settings"
msgstr ""
#: main.kv:178
msgid "Purchase"
msgstr ""
#: main.kv:184
msgid "New address"
msgstr ""
#: main.kv:191
msgid "Network status"
msgstr ""
#: main.kv:197
msgid "My addresses"
msgstr ""
#: kv/chat_list.kv:14
msgid "Chats"
msgstr ""
#: kv/chat_list.kv:26
msgid "No Chat"
msgstr ""
#: kv/chat_list.kv:50
msgid "Contacts"
msgstr ""
#: kv/chat_room.kv:40
msgid "Send"
msgstr ""
#: kv/credits.kv:10
msgid "Available Credits"
msgstr ""
#: kv/credits.kv:27
msgid "+Add more credits"
msgstr ""
#: kv/login.kv:31
msgid "Select method to make an address:"
msgstr ""
#: kv/login.kv:57
msgid "Pseudorandom Generator"
msgstr ""
#: kv/login.kv:77
msgid "Passphrase (deterministic) Generator"
msgstr ""
#: kv/login.kv:81 kv/login.kv:188
msgid "Proceed Next"
msgstr ""
#: kv/login.kv:163
msgid "Enter a label to generate address for:"
msgstr ""
#: kv/maildetail.kv:31
msgid "to "
msgstr ""
#: kv/msg_composer.kv:64
msgid "Type or Scan QR code for recipients address"
msgstr ""
#: kv/myaddress.kv:17
msgid "My Addresses"
msgstr ""
#: kv/network.kv:8
msgid "Total connections"
msgstr ""
#: kv/network.kv:16
msgid "Total Connections"
msgstr ""
#: kv/network.kv:37
msgid "Processes"
msgstr ""
#: kv/network.kv:45
msgid "person-to-person"
msgstr ""
#: kv/network.kv:67
msgid "Brodcast"
msgstr ""
#: kv/network.kv:89
msgid "publickeys"
msgstr ""
#: kv/network.kv:111
msgid "objects"
msgstr ""
#: kv/popup.kv:28 kv/popup.kv:57 kv/popup.kv:109
msgid "Label"
msgstr ""
#: kv/popup.kv:38 kv/popup.kv:76 kv/popup.kv:121
msgid "Address"
msgstr ""
#: kv/popup.kv:149
msgid "Send message from"
msgstr ""
#: kv/popup.kv:160
msgid "Show QR code"
msgstr ""
#: kv/popup.kv:170 kv/popup.kv:231 kv/popup.kv:308 kv/settings.kv:598
#: kv/settings.kv:693 kv/settings.kv:943
msgid "Cancel"
msgstr ""
#: kv/popup.kv:196
msgid ""
"Bitmessage isn't connected to the network.\n"
" If you quit now, it may cause delivery delays.\n"
" Wait until connected and the synchronisation finishes?"
msgstr ""
#: kv/popup.kv:210
msgid "Yes"
msgstr ""
#: kv/popup.kv:220
msgid "No"
msgstr ""
#: kv/popup.kv:255
msgid "From :"
msgstr ""
#: kv/popup.kv:269
msgid "[b]"
msgstr ""
#: kv/popup.kv:295
msgid "Date : "
msgstr ""
#: kv/settings.kv:8
msgid "User Interface"
msgstr ""
#: kv/settings.kv:30
msgid "Start-on-login not yet supported on your OS"
msgstr ""
#: kv/settings.kv:47
msgid "Tray"
msgstr ""
#: kv/settings.kv:61
msgid "Start Bitmessage in the tray(don't show main window)"
msgstr ""
#: kv/settings.kv:75
msgid "Minimize to tray"
msgstr ""
#: kv/settings.kv:89
msgid "Close to tray"
msgstr ""
#: kv/settings.kv:106
msgid "Hide connection notifications"
msgstr ""
#: kv/settings.kv:119
msgid "Show notification when message received"
msgstr ""
#: kv/settings.kv:127
msgid ""
"In portable Mode, messages and config files are stored in the same directory "
"as the program rather then the normal application-data folder. This makes it "
"convenient to run Bitmessage from a USB thumb drive."
msgstr ""
#: kv/settings.kv:144
msgid ""
"Willingly include unencrypted destination address when sending to a mobile "
"device"
msgstr ""
#: kv/settings.kv:158
msgid "Use identicons"
msgstr ""
#: kv/settings.kv:171
msgid "Reply below Quote"
msgstr ""
#: kv/settings.kv:187
msgid "Interface Language"
msgstr ""
#: kv/settings.kv:204 kv/settings.kv:449 kv/settings.kv:600 kv/settings.kv:771
#: kv/settings.kv:945
msgid "Apply"
msgstr ""
#: kv/settings.kv:221
msgid "Listening port"
msgstr ""
#: kv/settings.kv:232
msgid "Listen for connections on port:"
msgstr ""
#: kv/settings.kv:239
msgid "8444"
msgstr ""
#: kv/settings.kv:254
msgid "UPnP"
msgstr ""
#: kv/settings.kv:262
msgid "Proxy server / Tor"
msgstr ""
#: kv/settings.kv:273
msgid "Type:"
msgstr ""
#: kv/settings.kv:292
msgid "Server hostname:"
msgstr ""
#: kv/settings.kv:297 kv/settings.kv:884
msgid "localhost"
msgstr ""
#: kv/settings.kv:304 kv/settings.kv:891
msgid "Port:"
msgstr ""
#: kv/settings.kv:317 kv/settings.kv:904
msgid "9050"
msgstr ""
#: kv/settings.kv:325 kv/settings.kv:912
msgid "Username:"
msgstr ""
#: kv/settings.kv:336
msgid "Pass:"
msgstr ""
#: kv/settings.kv:354
msgid "Authentication"
msgstr ""
#: kv/settings.kv:369
msgid "Listen for incoming connections when using proxy"
msgstr ""
#: kv/settings.kv:384
msgid "Only connect to onion services(*.onion)"
msgstr ""
#: kv/settings.kv:392
msgid "Bandwidth limit"
msgstr ""
#: kv/settings.kv:403
msgid "Maximum download rate (kB/s):[0:unlimited]"
msgstr ""
#: kv/settings.kv:408 kv/settings.kv:742
msgid "0"
msgstr ""
#: kv/settings.kv:419
msgid "Maximum upload rate (kB/s):[0:unlimited]"
msgstr ""
#: kv/settings.kv:435
msgid "Maximum outbound connections:[0:none]"
msgstr ""
#: kv/settings.kv:494
msgid "Total difficulty:"
msgstr ""
#: kv/settings.kv:499 kv/settings.kv:527 kv/settings.kv:658
msgid "00000.0"
msgstr ""
#: kv/settings.kv:522
msgid "Small message difficulty:"
msgstr ""
#: kv/settings.kv:653
msgid "Maximum acceptable total difficulty:"
msgstr ""
#: kv/settings.kv:667
msgid "Hardware GPU acceleration (OpenCL):"
msgstr ""
#: kv/settings.kv:695 kv/settings.kv:947
msgid "OK"
msgstr ""
#: kv/settings.kv:725
msgid "Leave these input fields blank for the default behavior."
msgstr ""
#: kv/settings.kv:737
msgid "Give up after"
msgstr ""
#: kv/settings.kv:748
msgid "days and"
msgstr ""
#: kv/settings.kv:833
msgid "Connect to:"
msgstr ""
#: kv/settings.kv:849
msgid "Namecoind"
msgstr ""
#: kv/settings.kv:860
msgid "NMControl"
msgstr ""
#: kv/settings.kv:879
msgid "hostname:"
msgstr ""
#: kv/settings.kv:923
msgid "Password:"
msgstr ""

16
app/requirements.txt Normal file
View File

@ -0,0 +1,16 @@
asttokens==2.2.1
certifi==2022.12.7
charset-normalizer==2.1.1
click==8.1.3
future==0.18.2
idna==3.4
parsimonious==0.10.0
pyseeyou==1.0.2
python-dotenv==0.21.0
pytz==2022.6
regex==2022.10.31
requests==2.28.1
six==1.16.0
toolz==0.12.0
transifex-python==3.0.3
urllib3==1.26.13

15
app/transifex_demo.py Normal file
View File

@ -0,0 +1,15 @@
"""Transifex basic implementation using python native sdk """
import os
from transifex.native import init, tx
from transifex.native.parsing import SourceString
token = os.getenv('token')
secret = os.getenv('secret')
print("token value",token)
init(token=token, languages=['el', 'fr', 'en'], secret=secret)
# Add some strings to push
strings = [SourceString('My Addresses')]
response_content = tx.push_source_strings(strings)
tx.fetch_translations()
el_translation = tx.translate('My Addresses', 'fr')
print("here is the translation",el_translation)

10
docker-compose.yml Normal file
View File

@ -0,0 +1,10 @@
# docker-compose.yml
version: '2'
services:
service-name:
image: transifex-api-docker
# environment:
# - GREETING=hello
env_file:
- transifex.env

6
flask_webhook/Dockerfile Normal file
View File

@ -0,0 +1,6 @@
FROM python:3
ADD /webhook_app/flask_webhook_requirements.txt /
RUN pip install -r flask_webhook_requirements.txt
ADD /webhook_app/server.py /
CMD [ "python", "server.py" ]

View File

@ -0,0 +1,13 @@
# docker-compose.yml
version: '2'
services:
service-name:
image: demo_api
# environment:
# - GREETING=hello

View File

@ -0,0 +1,22 @@
asttokens==2.2.1
certifi==2022.12.7
charset-normalizer==2.1.1
click==8.1.3
Flask==2.2.2
future==0.18.2
idna==3.4
importlib-metadata==5.2.0
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.1
parsimonious==0.10.0
pyseeyou==1.0.2
pytz==2022.7
regex==2022.10.31
requests==2.28.1
six==1.16.0
toolz==0.12.0
transifex-python==3.0.3
urllib3==1.26.13
Werkzeug==2.2.2
zipp==3.11.0

View File

@ -0,0 +1,15 @@
from flask import Flask, request, abort
app = Flask(__name__)
@app.route('/webhook', methods=['POST'])
def webhook():
if request.method == 'POST':
print(request.json)
return 'success', 200
else:
abort(400)
if __name__ == '__main__':
app.run()

View File

@ -0,0 +1,69 @@
# import requests
# import json
# webhook_url = 'http://127.0.0.1:5000/webhook'
# data = { 'name': 'Request three',
# 'Channel URL': 'test url3' }
# r = requests.post(webhook_url, data=json.dumps(data), headers={'Content-Type': 'application/json'})
kv_strings1 = [
"Accounts", "Inbox", "Sent", "Draft", "Trash", "All Mails", "All labels", "Address Book", "Settings", "Purchase", "New address", "Network status"
"My addresses", "Chats", "No Chat", "Contacts", "Send", "Available Credits", "+Add more credits", "Select method to make an address",
"Pseudorandom Generator", "Passphrase (deterministic) Generator", "Proceed Next", "Enter a label to generate address for",
"to ", "Type or Scan QR code for recipients address", "My Addresses", "Total connections", "Total Connections", "Processes",
"person-to-person", "Brodcast", "publickeys", "objects", "Label", "Address", "Send message from", "Show QR code", "Cancel",
"Yes", "No", "From ", "[b]", "Date ", "User Interface", "Start-on-login not yet supported on your OS", "Tray"]
kv_strings2 = ["Start Bitmessage in the tray(don't show main window)", "Minimize to tray", "Close to tray", "Hide connection notifications",
"Show notification when message received", "Use identicons", "Reply below Quote", "Interface Language", "Apply", "Listening port",
"Listen for connections on port", "8444", "UPnP", "Proxy server / Tor", "Type", "Server hostname", "localhost", "Port", "9050", "Username",
"Pass", "Authentication", "Listen for incoming connections when using proxy", "Only connect to onion services(*.onion)", "Bandwidth limit"]
kv_strings3 = ["Maximum download rate (kB/s):[0:unlimited]", "0", "Maximum upload rate (kB/s):[0:unlimited]", "Maximum outbound connections:[0:none]",
"Total difficulty", "00000.0", "Small message difficulty", "Maximum acceptable total difficulty",
"Hardware GPU acceleration (OpenCL)", "OK", "Leave these input fields blank for the default behavior.",
"Give up after", "days and", "Connect to", "Namecoind", "NMControl", "hostname", "Password"]
demo_str = ['hello', 'this', 'is']
translated_dict = {}
from deep_translator import GoogleTranslator
for string in kv_strings1:
try:
int(string)
except:
to_translate = string
translated = GoogleTranslator(source='auto', target='fr').translate(to_translate)
translated_dict[string] = translated
# for string in kv_strings2:
# to_translate = string
# translated = GoogleTranslator(source='auto', target='fr').translate(to_translate)
# translated_dict[string] = translated
# for string in kv_strings3:
# to_translate = string
# translated = GoogleTranslator(source='auto', target='fr').translate(to_translate)
# translated_dict[string] = translated
print(translated_dict)
{
'Accounts': 'Comptes', 'Inbox': 'Boîte de réception', 'Sent': 'Envoyé', 'Draft': 'Brouillon', 'Trash': 'Déchets', 'All Mails': 'Tous les e-mails',
'All labels': 'Toutes les étiquettes', 'Address Book': "Carnet d'adresses", 'Settings': 'Réglages', 'Purchase': 'Acheter', 'New address': 'Nouvelle adresse',
'Network statusMy addresses': 'État du réseauMes adresses', 'Chats': 'Chats', 'No Chat': 'Pas de message texte', 'Contacts': 'Contacts', 'Send': 'Envoyer',
'Available Credits': 'Crédits disponibles', '+Add more credits': '+Ajouter plus de crédits',
'Select method to make an address': 'Sélectionnez la méthode pour créer une adresse', 'Pseudorandom Generator': 'Générateur pseudo-aléatoire',
'Passphrase (deterministic) Generator': 'Générateur de phrase de passe (déterministe)', 'Proceed Next': 'Procéder ensuite',
'Enter a label to generate address for': "Entrez une étiquette pour générer l'adresse pour", 'to ': 'à',
'Type or Scan QR code for recipients address': "Tapez ou scannez le code QR pour l'adresse des destinataires",
'My Addresses': 'Mes adresses', 'Total connections': 'Connexions totales', 'Total Connections': 'Connexions totales',
'Processes': 'Processus', 'person-to-person': 'personne à personne', 'Brodcast': 'Diffusion', 'publickeys': 'clés publiques',
'objects': 'objets', 'Label': 'Étiquette', 'Address': 'Adresse', 'Send message from': 'Envoyer un message de', 'Show QR code': 'Afficher le code QR',
'Cancel': 'Annuler', 'Yes': 'Oui', 'No': 'Non', 'From ': 'De', '[b]': '[c]', 'Date ': 'Date', 'User Interface': 'Interface utilisateur',
'Start-on-login not yet supported on your OS': "Start-on-login n'est pas encore pris en charge sur votre système d'exploitation", 'Tray': 'Plateau'
}

View File

@ -0,0 +1,6 @@
FROM python:3
ADD /flask_app/flask_requirement.txt /
RUN pip install -r flask_requirement.txt
ADD /flask_app/app.py /
CMD [ "python", "app.py" ]

View File

@ -0,0 +1,12 @@
# docker-compose.yml
version: '2'
services:
service-name:
image: flask_docker
# environment:
# - GREETING=hello
env_file:
- ../transifex.env

View File

@ -0,0 +1,181 @@
# import main Flask class and request object
from base64 import b64encode
import fcntl
# from http.client import sha1
from hashlib import sha1
import hmac
from http import client # import httplib
import json
import os
import pprint
import requests
import shutil
from subprocess import call, Popen
import sys
import tempfile
import time
import traceback
from urllib.parse import urlparse
import os
from flask import Flask, request
from transifex.api import transifex_api
from transifex.native import init, tx
from transifex.native.parsing import SourceString
# create the Flask app
app = Flask(__name__)
@app.route('/query-example')
def transifex_demo():
token = os.getenv('token')
secret = os.getenv('secret')
print("token value",token)
init(token=token, languages=['el', 'fr', 'en'], secret=secret)
# Add some strings to push
strings = [SourceString('My Addresses')]
response_content = tx.push_source_strings(strings)
tx.fetch_translations()
el_translation = tx.translate('My Addresses', 'fr')
return print(el_translation)
# @app.route('/commit-example')
# def commitTranslatedLanguage(ts, lang):
# call(["lrelease-qt4", "src/translations/bitmessage.pro"])
# call(["git", "add", "src/translations/bitmessage_" + lang + ".ts", "src/translations/bitmessage_" + lang + ".qm"])
# call(["git", "commit", "-q", "-S", "-m", "Auto-updated language %s from transifex" % (lang)])
# newbranch = "translate_" + lang + "_" + str(ts)
# call(["git", "push", "-q", "translations", newbranch + ":" + newbranch])
# request = {
# "title": "Translation update " + lang,
# "body": "Auto-updated from transifex",
# "head": "PyBitmessageTranslations:" + newbranch,
# "base": branch
# }
# headers = {"Authorization": "token " + gitHubToken}
# response = requests.post("https://api.github.com/repos/Bitmessage/PyBitmessage/pulls",
# headers=headers, data=json.dumps(request))
# # TODO: save pull request number
# return response
# # print "JSON dumps request: %s" % (json.dumps(request))
# # print "Response from github for pull request: %i, %s" % (response.status_code, response.content)
# def updateLocalTranslationSource():
# call(["git", "stash", "-q"])
# call(["git", "checkout", "-q", branch])
# call(["git", "pull", "-q"])
# call(["pylupdate4", "src/translations/bitmessage.pro"])
# def uploadTranslationSource():
# headers = {"Authorization": "Basic " + b64encode(transifexUsername + ":" + transifexPassword)}
# response = requests.put("https://www.transifex.com/api/2/project/pybitmessage/resource/pybitmessage/content/",
# headers=headers, files={'bitmessage_en.ts': open("src/translations/bitmessage_en.ts", "rb")})
# return response
@app.route('/upload-source')
def upload_source():
async_job(id)
return 'ok'
def async_job(id):
response = requests.get("http://www.transifex.com/bitmessage-project/pybitmessage/upload_source/$BUILDBOT_JOBID" + id +"/")
api_token = ''
transifex_api.setup(auth=api_token)
# transifex_api.Organization.list()
organization = transifex_api.Organization.get(slug="bitmessage-project")
project = organization.fetch('projects').get(slug="pybitmessage-test")
language = transifex_api.Language.get(code="fr")
resource = project.fetch('resources').get(slug="messagespot")
content = "The new source file content"
with open(resource,'rb') as f:
upload = transifex_api.ResourceStringsAsyncUpload.create_with_form(
data={'resource': resource.id},
files={'content': f},
)
return "done"
# transifex_api.ResourceStringsAsyncUpload.upload(resource, content)
while not response:
sleep_time = time.sleep(1)
sleep_time+=1
if sleep_time == 600:
return False
# def updateLocalTranslationDestination(ts, lang):
# call(["git", "pull", "--all", "-q"])
# call(["git", "stash", "-q"])
# call(["git", "checkout", "-q", branch])
# call(["git", "checkout", "-q", "-b", "translate_" + lang + "_" + str(ts)])
# call(["git", "branch", "-q", "--set-upstream-to=origin/v0.6"])
# def downloadTranslatedLanguage(ts, lang):
# headers = {"Authorization": "Basic " + b64encode(transifexUsername + ":" + transifexPassword)}
# resname = "pybitmessage_" + lang + ".ts"
# fname = "bitmessage_" + lang.lower() + ".ts"
# with open("src/translations/" + fname, "wt") as handle:
# response = requests.get("https://www.transifex.com/api/2/project/pybitmessage/resource/pybitmessage/translation/" + lang + "/",
# headers=headers)
# if response.ok:
# content = json.loads(response.content)["content"]
# handle.write(content.encode("utf-8"))
# # print "Response from github for pull request: %i, %s" % (response.status_code, response.content)
# return response
# def application():
# if "Transifex" in environ.get("HTTP_USER_AGENT"):
# # debug(environ)
# # debug(body)
# pass
# if not verifyTransifexSignature(environ, body):
# debug ("Verify Transifex Signature fail, but fuck them")
# else:
# debug ("Verify Transifex Signature ok")
# # output, responseHeaders = returnMessage(False, "Checksum bad")
# # start_response(status, responseHeaders)
# # unlock()
# # return [output]
# try:
# # debug(body)
# payload = parse_qs(body)
# # debug(payload)
# if 'pybitmessage' in payload['project'] and 'pybitmessage' in payload['resource']:
# if 'translated' in payload and '100' in payload['translated']:
# ts = int(time.time())
# updateLocalTranslationDestination(ts, payload['language'][0].lower())
# downloadTranslatedLanguage(ts, payload['language'][0])
# response = commitTranslatedLanguage(ts, payload['language'][0].lower())
# if response.ok:
# output, responseHeaders = returnMessage(True, "Processed.")
# else:
# output, responseHeaders = returnMessage(False, "Error: %i." % (response.status_code))
# else:
# output, responseHeaders = returnMessage(False, "Nothing to do")
# else:
# output, responseHeaders = returnMessage(False, "Nothing to do")
# except:
# output, responseHeaders = returnMessage(True, "Not processing")
# else:
# debug("Unknown command %s" % (environ.get("HTTP_X_GITHUB_EVENT")))
# output, responseHeaders = returnMessage(True, "Unknown command, ignoring")
# # output = ''
# # for k, v in environ.items():
# # output += '%.40s %s\n' % (k, v)
# #responseHeaders = sendFile("ffb8c8eb-3d3b-4306-b65e-e0d1fa4f7ea0")
# start_response(status, responseHeaders)
# unlock()
# return [output]
if __name__ == '__main__':
# run app in debug mode on port 5000
app.run(debug=True, port=5000)

View File

@ -0,0 +1,22 @@
asttokens==2.2.1
certifi==2022.12.7
charset-normalizer==2.1.1
click==8.1.3
Flask==2.2.2
future==0.18.2
idna==3.4
importlib-metadata==5.2.0
itsdangerous==2.1.2
Jinja2==3.1.2
MarkupSafe==2.1.1
parsimonious==0.10.0
pyseeyou==1.0.2
pytz==2022.7
regex==2022.10.31
requests==2.28.1
six==1.16.0
toolz==0.12.0
transifex-python==3.0.3
urllib3==1.26.13
Werkzeug==2.2.2
zipp==3.11.0

3
transifex_upload.sh Executable file
View File

@ -0,0 +1,3 @@
pip install transifex-client
# chmod 777 '/home/cis/PyBitmessage/transifex/transifex-webhook/.tx/config'
tx init

BIN
tx Executable file

Binary file not shown.