Files
2026-01-16 12:10:33 +03:00

117 lines
4.6 KiB
Python

#!/usr/bin/env python
# coding=utf-8
"""Python implementation of Tinode command line client using gRPC."""
from __future__ import print_function
import argparse
import os
import platform
import sys
try:
from importlib.metadata import version
except ImportError:
# Fallback for Python < 3.8
from importlib_metadata import version
import tn_globals
from tn_globals import printout
from client import run, read_cookie
from commands import set_macros_module
APP_NAME = "tn-cli"
APP_VERSION = "3.1.0" # format: 1.9.0b1
LIB_VERSION = version("tinode_grpc")
GRPC_VERSION = version("grpcio")
# This is needed for gRPC SSL to work correctly.
os.environ["GRPC_SSL_CIPHER_SUITES"] = "HIGH+ECDSA"
# Setup crash handler: close input reader otherwise a crash
# makes terminal session unusable.
def exception_hook(type, value, traceBack):
if tn_globals.InputThread != None:
tn_globals.InputThread.join(0.3)
sys.excepthook = exception_hook
# Enable the following variables for debugging.
# os.environ["GRPC_TRACE"] = "all"
# os.environ["GRPC_VERBOSITY"] = "INFO"
if __name__ == '__main__':
"""Parse command-line arguments. Extract host name and authentication scheme, if one is provided"""
version_str = APP_VERSION + "/" + LIB_VERSION + "; gRPC/" + GRPC_VERSION + "; Python " + platform.python_version()
purpose = "Tinode command line client. Version " + version_str + "."
parser = argparse.ArgumentParser(description=purpose)
parser.add_argument('--host', default='localhost:16060', help='address of Tinode gRPC server')
parser.add_argument('--web-host', default='localhost:6060', help='address of Tinode web server (for file uploads)')
parser.add_argument('--ssl', action='store_true', help='connect to server over secure connection')
parser.add_argument('--ssl-host', help='SSL host name to use instead of default (useful for connecting to localhost)')
parser.add_argument('--login-basic', help='login using basic authentication username:password')
parser.add_argument('--login-token', help='login using token authentication')
parser.add_argument('--login-cookie', action='store_true', help='read token from cookie file and use it for authentication')
parser.add_argument('--no-login', action='store_true', help='do not login even if cookie file is present; default in non-interactive (scripted) mode')
parser.add_argument('--no-cookie', action='store_true', help='do not save login cookie; default in non-interactive (scripted) mode')
parser.add_argument('--api-key', default='AQEAAAABAAD_rAp4DJh05a1HAwFT3A6K', help='API key for file uploads')
parser.add_argument('--load-macros', default='./macros.py', help='path to macro module to load')
parser.add_argument('--version', action='store_true', help='print version')
parser.add_argument('--verbose', action='store_true', help='log full JSON representation of all messages')
parser.add_argument('--background', action='store_const', const=True, help='start interactive sessionin background (non-intractive is always in background)')
args = parser.parse_args()
if args.version:
printout(version_str)
exit()
if args.verbose:
tn_globals.Verbose = True
printout(purpose)
printout("Secure server" if args.ssl else "Server", "at '"+args.host+"'",
"SNI="+args.ssl_host if args.ssl_host else "")
schema = None
secret = None
if not args.no_login:
if args.login_token:
"""Use token to login"""
schema = 'token'
secret = args.login_token.encode('ascii')
printout("Logging in with token", args.login_token)
elif args.login_basic:
"""Use username:password"""
schema = 'basic'
secret = args.login_basic
printout("Logging in with login:password", args.login_basic)
elif tn_globals.IsInteractive:
"""Interactive mode only: try reading the cookie file"""
printout("Logging in with cookie file")
schema = 'token'
secret = read_cookie()
if not secret:
schema = None
# Attempt to load the macro file if available.
macros = None
if args.load_macros:
import importlib
macros = importlib.import_module('macros', args.load_macros) if args.load_macros else None
set_macros_module(macros)
# Check if background session is specified explicitly. If not set it to
# True for non-interactive sessions.
if args.background is None and not tn_globals.IsInteractive:
args.background = True
sys.exit(run(args, schema, secret))