├── .gitignore ├── LICENSE ├── README.md ├── cloudclip ├── cloudclip.cmd └── cloudclip.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Linwei 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CloudClip 2 | 3 | Your own clipboard in the cloud, copy and paste text with gist. Inspired by pbcopy/pbpaste from Mac OS X. 4 | 5 | ## Tutorial 6 | 7 | Copy text on a remote system to the cloud: 8 | 9 | echo "Hello, Cloud Clipboard" | cloudclip -c 10 | 11 | Paste on a local system from the cloud: 12 | 13 | cloudclip -p 14 | 15 | And you will see the text coppied from the remote system: 16 | 17 | Hello, Cloud Clipboard 18 | 19 | Same access token and gist id must be setup before copy/paste, see below 20 | 21 | 22 | ## Installation 23 | 24 | Clone the repository from github: 25 | 26 | ```bash 27 | git clone https://github.com/skywind3000/CloudClip.git 28 | ``` 29 | 30 | Add repository path to your `$PATH`: 31 | 32 | For linux, add these line at the bottom of your `.bashrc`: 33 | 34 | export PATH="/path-to-cloud-clip:$PATH" 35 | 36 | For Windows: 37 | 38 | Manully setup the PATH in your control panel. 39 | 40 | ## Documentation 41 | 42 | usage: python cloudclip.py [...] 43 | operations: 44 | 45 | ```bash 46 | -i [id] Initialize token and id, create a new gist if id is empty 47 | -c [name] Takes the standard input and places it in the cloud 48 | -p [name] Read content from cloud and output to standard output 49 | -l List information of the gist 50 | -e Clean the clipboard 51 | ``` 52 | 53 | A github access token is needed before everything, you can generate a new one from: https://github.com/settings/tokens 54 | 55 | Create a new gist with "-i token" on your own PC, remember the gist id. then use "-i token id" on a remote one which you may exchange data with. 56 | 57 | ## Aliases 58 | 59 | For convenience, two aliases can be created: 60 | 61 | ```bash 62 | alias cloudcopy='python /path/to/cloudclip.py -c' 63 | alias cloudpaste='python /path/to/cloudclip.py -p' 64 | ``` 65 | 66 | ## Requirement 67 | 68 | * Python 2 69 | * [requests](http://www.python-requests.org/en/master/) 70 | 71 | Install requests with pip: 72 | 73 | pip install requests 74 | 75 | ## Credit 76 | 77 | TODO 78 | -------------------------------------------------------------------------------- /cloudclip: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | 4 | MY_PATH="`dirname \"$0\"`" 5 | MY_PATH="`( cd \"$MY_PATH\" && pwd )`" 6 | 7 | env python "$MY_PATH/cloudclip.py" $@ 8 | 9 | -------------------------------------------------------------------------------- /cloudclip.cmd: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | set "CCHOME=%~dp0" 4 | 5 | python -u "%CCHOME%cloudclip.py" %* 6 | 7 | 8 | -------------------------------------------------------------------------------- /cloudclip.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | #====================================================================== 4 | # 5 | # cloudclip.py - copy/paste text in the cloud (with gist as backend) 6 | # 7 | # Created by skywind on 2018/01/23 8 | # Last change: 2018/01/23 20:52:10 9 | # 10 | #====================================================================== 11 | import sys 12 | import os 13 | import json 14 | import time 15 | import ConfigParser 16 | import requests 17 | 18 | 19 | #---------------------------------------------------------------------- 20 | # GistError 21 | #---------------------------------------------------------------------- 22 | class GistError (StandardError): 23 | def __init__ (self, code, what): 24 | super(StandardError, self).__init__(what) 25 | self.code = code 26 | 27 | 28 | #---------------------------------------------------------------------- 29 | # GistRequest 30 | #---------------------------------------------------------------------- 31 | class GistRequest (object): 32 | 33 | def __init__ (self, token, options = {}): 34 | self.session = None 35 | self.options = {} 36 | if options: 37 | for k in options: 38 | self.options[k] = options[k] 39 | self.https = self.options.get('https', True) 40 | self.token = token 41 | self.code = 0 42 | self.error = None 43 | self.res = None 44 | 45 | def request (self, url, method, head = {}, params = {}, data = None): 46 | if self.session is None: 47 | self.session = requests.Session() 48 | if self.https: 49 | base = 'https://api.github.com/' 50 | else: 51 | base = 'http://api.github.com/' 52 | if url[:1] == '/': 53 | url = url[1:] 54 | url = base + url 55 | s = self.session 56 | argv = {} 57 | if 'timeout' in self.options: 58 | argv['timeout'] = self.options['timeout'] 59 | if 'proxies' in self.options: 60 | argv['proxies'] = self.options['proxies'] 61 | p = {} 62 | if params: 63 | for k in params: 64 | p[k] = params[k] 65 | headers = {} 66 | if head: 67 | for k in head: 68 | headers[k] = head[k] 69 | if self.token: 70 | headers['Authorization'] = 'token %s'%self.token 71 | if p: 72 | argv['params'] = p 73 | if data is not None: 74 | argv['data'] = data 75 | if headers: 76 | argv['headers'] = headers 77 | method = method.lower() 78 | if method == 'get': 79 | r = s.get(url, **argv) 80 | elif method == 'post': 81 | r = s.post(url, **argv) 82 | elif method == 'patch': 83 | r = s.patch(url, **argv) 84 | elif method == 'put': 85 | r = s.patch(url, **argv) 86 | elif method == 'delete': 87 | r = s.delete(url, **argv) 88 | else: 89 | raise GistError(-100, 'Bad method') 90 | return r 91 | 92 | def request_gist (self, url, method, head = {}, param = {}, data = None): 93 | self.res = None 94 | if data: 95 | if not isinstance(data, str): 96 | data = json.dumps(data) 97 | r = self.request(url, method, head, param, data) 98 | if r is None: 99 | raise GistError(-100, 'Unknow error') 100 | return None 101 | self.res = r 102 | if not r.status_code in (200, 201, 204): 103 | self.code = r.status_code 104 | self.text = r.__dict__.get('text', None) 105 | self.error = r.__dict__.get('error', None) 106 | message = 'HTTP error code=%d: %s'%(r.status_code, r.text) 107 | raise GistError(r.status_code, message) 108 | return None 109 | self.code = 0 110 | self.text = r.text 111 | self.error = None 112 | text = self.text 113 | try: 114 | obj = r.json() 115 | except: 116 | return None 117 | return obj 118 | 119 | def get (self, url, headers = {}, params = {}, data = None): 120 | return self.request_gist(url, 'GET', headers, params, data) 121 | 122 | def put (self, url, headers = {}, params = {}, data = None): 123 | return self.request_gist(url, 'PUT', headers, params, data) 124 | 125 | def post (self, url, headers = {}, params = {}, data = None): 126 | return self.request_gist(url, 'POST', headers, params, data) 127 | 128 | def patch (self, url, headers = {}, params = {}, data = None): 129 | return self.request_gist(url, 'PATCH', headers, params, data) 130 | 131 | def delete (self, url, headers = {}, params = {}, data = None): 132 | return self.request_gist(url, 'DELETE', headers, params, data) 133 | 134 | 135 | #---------------------------------------------------------------------- 136 | # empty object 137 | #---------------------------------------------------------------------- 138 | class GistObject (object): 139 | 140 | def __init__ (self, gistid): 141 | self.gistid = gistid 142 | self.description = '' 143 | self.ctime = None 144 | self.mtime = None 145 | self.files = [] 146 | 147 | 148 | #---------------------------------------------------------------------- 149 | # gist api 150 | #---------------------------------------------------------------------- 151 | class GistApi (object): 152 | 153 | def __init__ (self, username, token): 154 | self.username = username 155 | self.token = token 156 | self.request = GistRequest(token) 157 | self.request.options['timeout'] = 20 158 | self.request.https = True 159 | self.error_msg = None 160 | self.error_code = 0 161 | 162 | # since: ISO 8601 format: YYYY-MM-DDTHH:MM:SSZ 163 | def list (self, since = None): 164 | if self.username: 165 | url = '/users/%s/gists'%self.username 166 | else: 167 | url = '/gists' 168 | params = {} 169 | if since: 170 | params['since'] = since 171 | r = self.request.get(url) 172 | return r 173 | 174 | def get (self, gistid, version = None): 175 | url = '/gists/' + gistid 176 | if version: 177 | url += '/' + version 178 | r = self.request.get(url) 179 | return r 180 | 181 | def create (self, description, files, public = False): 182 | data = {} 183 | if description: 184 | data['description'] = description 185 | data['public'] = public 186 | if files is None: 187 | raise GistError(-101, 'files filed required') 188 | data['files'] = files 189 | params = {'scope': 'gist'} 190 | r = self.request.post('/gists', {}, params, data) 191 | return r 192 | 193 | def edit (self, gistid, description, files): 194 | data = {} 195 | if description: 196 | data['description'] = description 197 | data['files'] = files 198 | params = {'scope': 'gist'} 199 | r = self.request.patch('/gists/' + gistid, {}, params, data) 200 | return r 201 | 202 | def delete (self, gistid): 203 | r = self.request.delete('/gists/' + gistid) 204 | return r 205 | 206 | def gist_get (self, gistid): 207 | r = self.get(gistid) 208 | files = r['files'] 209 | gist = GistObject(gistid) 210 | gist.ctime = r.get('created_at', '') 211 | gist.mtime = r.get('updated_at', '') 212 | gist.files = {} 213 | gist.owner = r.get('owner', {}).get('login', 'unknow') 214 | gist.description = r.get('description', None) 215 | for name in files: 216 | data = files[name] 217 | obj = {} 218 | obj['size'] = data.get('size', -1) 219 | obj['type'] = data.get('type', None) 220 | obj['truncated'] = data.get('truncated', False) 221 | obj['language'] = data.get('language', '') 222 | gist.files[name] = data 223 | return gist 224 | 225 | def gist_update (self, gist): 226 | r = self.edit(gist.gistid, gist.description, gist.files) 227 | return r 228 | 229 | 230 | #---------------------------------------------------------------------- 231 | # CloudClip 232 | #---------------------------------------------------------------------- 233 | class CloudClip (object): 234 | 235 | def __init__ (self, ininame): 236 | self.api = GistApi('', token) 237 | self.config = {} 238 | if '~' in ininame: 239 | ininame = os.path.expanduser(ininame) 240 | self.ininame = os.path.normcase(ininame) 241 | self.read_ini(self.ininame) 242 | self.set_token(self.config.get('token', None)) 243 | self.set_id(self.config.get('id', None)) 244 | 245 | def set_token (self, token): 246 | if token: 247 | token = token.strip('\r\n\t ').replace('\n', '') 248 | self.config['token'] = token 249 | self.api.request.token = token 250 | 251 | def get_token (self): 252 | return self.api.request.token 253 | 254 | def set_id (self, gistid): 255 | if gistid: 256 | gistid = gistid.strip('\r\n\t ').replace('\n', '') 257 | self.config['id'] = gistid 258 | self.gistid = gistid 259 | 260 | def get_id (self): 261 | return self.gistid 262 | 263 | def read_ini (self, ininame): 264 | if not os.path.exists(ininame): 265 | return False 266 | cp = ConfigParser.ConfigParser() 267 | try: 268 | cp.read(ininame) 269 | except: 270 | return False 271 | self.config = {} 272 | for sect in cp.sections(): 273 | if sect.lower() != 'default': 274 | continue 275 | for key, val in cp.items(sect): 276 | self.config[key.lower()] = val 277 | self.config['token'] = self.config.get('token', '') 278 | self.config['id'] = self.config.get('id', '') 279 | self.config['public'] = self.config.get('public', True) 280 | self.set_token(self.config['token'].strip('\r\n\t ')) 281 | self.set_id(self.config['id'].strip('\r\n\t ')) 282 | return True 283 | 284 | def login (self, token, gistid): 285 | self.set_token(token) 286 | self.set_id(gistid) 287 | create_gist = False 288 | if (not gistid) or (gistid == '-'): 289 | files = {} 290 | text = 'CloudClip:\n' 291 | text += 'Your own clipboard in the cloud, ' 292 | text += 'copy and paste text with gist between systems.\n' 293 | text += 'home: https://github.com/skywind3000/CloudClip\n\n' 294 | text += 'Place-holder, don\'t remove it !!' 295 | files[''] = {'content': text} 296 | r = self.api.create('', files) 297 | gistid = r['id'] 298 | self.set_id(gistid) 299 | print 'New gist created with id: ' + gistid 300 | create_gist = True 301 | gist = self.api.gist_get(self.gistid) 302 | with open(self.ininame, 'w') as fp: 303 | fp.write('[default]\n') 304 | fp.write('token=%s\n'%self.get_token()) 305 | fp.write('id=%s\n'%self.get_id()) 306 | print 'Configuration updated in: %s'%self.ininame 307 | if create_gist: 308 | print '' 309 | print 'Use the command below in other systems to initialize' 310 | print 'cloudclip.py -i %s %s'%(token, gistid) 311 | print '' 312 | return True 313 | 314 | def error (self, code, message): 315 | sys.stderr.write('Error: ' + message + '\n') 316 | sys.stderr.flush() 317 | sys.exit(code) 318 | 319 | def check (self): 320 | nm = sys.argv[0] 321 | if not os.path.exists(self.ininame) and False: 322 | text = "Authorization token and gist-id are required, see %s -h"%nm 323 | self.error(1, text) 324 | if not self.config['token']: 325 | text = "Authorization token is required, see %s -h"%nm 326 | self.error(2, text) 327 | if not self.config['id']: 328 | text = 'gist-id is required, see %s -h'%nm 329 | self.error(3, text) 330 | return True 331 | 332 | def list_info (self): 333 | self.check() 334 | gist = self.api.gist_get(self.gistid) 335 | ctime = gist.ctime.replace('T', ' ').replace('Z', '') 336 | mtime = gist.mtime.replace('T', ' ').replace('Z', '') 337 | print '%s: %s modified at %s'%(gist.gistid, gist.owner, mtime) 338 | size1 = 10 339 | size2 = 8 340 | names = gist.files.keys() 341 | names.sort() 342 | count = 0 343 | for name in names: 344 | item = gist.files[name] 345 | size = str(item['size']) 346 | size1 = max(size1, len(name)) 347 | size2 = max(size2, len(size)) 348 | if name == '': 349 | continue 350 | count += 1 351 | if count == 0: 352 | print '(empty)' 353 | return True 354 | print '' 355 | print 'Name'.ljust(size1), '\t', 'Size'.rjust(size2) 356 | print '----'.ljust(size1), '\t', '----'.rjust(size2) 357 | for name in names: 358 | item = gist.files[name] 359 | if name == '': 360 | continue 361 | print name.ljust(size1), '\t' + str(item['size']).rjust(size2) 362 | print '' 363 | print '(%d files)'%count 364 | return True 365 | 366 | def write_file (self, name, content, mime = None): 367 | self.check() 368 | gist = GistObject(self.gistid) 369 | gist.description = '' 370 | gist.files = {} 371 | data = {'content': content} 372 | if mime: 373 | data['type'] = mime 374 | if not name: 375 | name = '' 376 | gist.files[name] = data 377 | self.api.gist_update(gist) 378 | return True 379 | 380 | def read_file (self, name): 381 | self.check() 382 | gist = self.api.gist_get(self.gistid) 383 | if not name: 384 | name = '' 385 | if not name in gist.files: 386 | return None 387 | return gist.files[name]['content'] 388 | 389 | def clear (self): 390 | self.check() 391 | gist = self.api.gist_get(self.gistid) 392 | files = {} 393 | for name in gist.files: 394 | if name == '': 395 | continue 396 | files[name] = None 397 | gist.files = files 398 | self.api.gist_update(gist) 399 | return True 400 | 401 | def copy (self, name): 402 | content = sys.stdin.read() 403 | self.write_file(name, content) 404 | return 0 405 | 406 | def paste (self, name): 407 | content = self.read_file(name) 408 | if content is None: 409 | if not name: 410 | name = '' 411 | self.error(4, 'File not find: ' + name) 412 | sys.stdout.write(content) 413 | sys.stdout.flush() 414 | return 0 415 | 416 | 417 | 418 | #---------------------------------------------------------------------- 419 | # main 420 | #---------------------------------------------------------------------- 421 | def main(args = None): 422 | args = args and args or sys.argv 423 | args = [ n for n in args ] 424 | program = len(args) > 0 and args[0] or 'cloudclip.py' 425 | if len(args) < 2 or args[1] in ('-h', '--help'): 426 | print 'usage: %s [...]'%program 427 | print 'operations:' 428 | head = ' ' + program 429 | # print head, '{-i --init} token [id]' 430 | # print head, '{-c --copy} [name]' 431 | # print head, '{-p --paste} [name]' 432 | # print head, '{-l --list}' 433 | # print head, '{-e --clean}' 434 | print '' 435 | print '-i [id] Initialize token and id, create a new gist if id is empty' 436 | print '-c [name] Takes the standard input and places it in the cloud' 437 | print '-p [name] Read content from cloud and output to standard output' 438 | print '-l List information of the gist' 439 | print '-e Clean the clipboard' 440 | print '' 441 | print 'A github access token is needed before everything, you can generate a new' 442 | print 'one from: https://github.com/settings/tokens' 443 | print '' 444 | print 'Create a new gist with "-i token" on your own PC, remember the gist id.' 445 | print 'then use "-i token id" on a remote one which you may exchange data with.' 446 | print '' 447 | return 0 448 | 449 | cmd = args[1] 450 | 451 | if not os.path.exists(os.path.expanduser('~/.config')): 452 | os.mkdir(os.path.expanduser('~/.config')) 453 | 454 | cp = CloudClip('~/.config/cloudclip.conf') 455 | 456 | # check token/id from system environ 457 | env_token = os.environ.get('CLOUDCLIP_TOKEN', '') 458 | env_gistid = os.environ.get('CLOUDCLIP_ID', '') 459 | 460 | if env_token: 461 | cp.set_token(env_token) 462 | if env_gistid: 463 | cp.set_id(env_gistid) 464 | 465 | if (not os.path.exists(cp.ininame)) and (not cp.config['token']): 466 | if not cmd in ('-i', '--init'): 467 | text = 'uses "%s -i" to initialize your token\n'%program 468 | text += 'get a new token from: https://github.com/settings/tokens' 469 | cp.error(4, text) 470 | return 4 471 | elif not cp.config['id']: 472 | text = 'uses "%s -i" to indicate or create a gist id'%program 473 | cp.error(5, text) 474 | return 5 475 | 476 | try: 477 | if cmd in ('-i', '--init'): 478 | if len(args) < 3: 479 | cp.error(6, 'missing token, see "%s -h"'%program) 480 | return 6 481 | token = args[2] 482 | gistid = len(args) >= 4 and args[3] or None 483 | cp.login(token, gistid) 484 | return 0 485 | 486 | if cmd in ('-c', '--copy'): 487 | name = len(args) >= 3 and args[2] or None 488 | cp.copy(name) 489 | return 0 490 | 491 | if cmd in ('-p', '--paste'): 492 | name = len(args) >= 3 and args[2] or None 493 | cp.paste(name) 494 | return 0 495 | 496 | if cmd in ('-l', '--list'): 497 | cp.list_info() 498 | return 0 499 | 500 | if cmd in ('-e', '--clean'): 501 | cp.clear() 502 | return 0 503 | 504 | cp.error(7, 'unknow command: ' + cmd) 505 | 506 | except GistError as e: 507 | text = 'unknow' 508 | if e.code == 401: 509 | text = 'Bad credentials, token may be invalid\n' 510 | text += 'uses "%s -h" to see the help'%program 511 | elif e.code == 404: 512 | text = 'File not find, are you using the right gist id ?\n' 513 | text += 'uses "%s -h" to see the help'%program 514 | cp.error(8, text) 515 | 516 | return 0 517 | 518 | 519 | 520 | #---------------------------------------------------------------------- 521 | # testing case 522 | #---------------------------------------------------------------------- 523 | if __name__ == '__main__': 524 | token = '' 525 | ga = GistApi('skywind3000', token) 526 | # ga.https = False 527 | 528 | def test1(): 529 | r = ga.list() 530 | for item in r: 531 | print item['id'], item['description'] 532 | id = '4efdb6975821b180310174a2e7dc9581' 533 | # id = 'alksdjflajds' 534 | # print '' 535 | r = ga.get(id) 536 | print json.dumps(r, indent = 2) 537 | return 0 538 | 539 | def test2(): 540 | files = {} 541 | files['hello.txt'] = {'content': 'Hello, World !!'} 542 | files['abc.txt'] = {'content': 'abc'} 543 | r = ga.create('', files) 544 | print json.dumps(r, indent = 2) 545 | 546 | def test3(): 547 | files = {} 548 | files['hello.txt'] = {'content': 'Hello, World !!\x00234234'} 549 | files['abc.txt'] = {'content': 'abc: ' + str(time.time())} 550 | files[''] = {'content': '3'} 551 | id = 'f1c7b8aa521c4634e9ad4882fedfad8c' 552 | r = ga.edit(id, '', files) 553 | print json.dumps(r, indent = 2) 554 | 555 | def test4(): 556 | id = '4792620aea356a437d57aadd5e579500' 557 | r = ga.delete(id) 558 | print json.dumps(r, indent = 2) 559 | 560 | def test5(): 561 | cc = CloudClip('~/.config/cloudclip.conf') 562 | cc.login(token, '') 563 | return 0 564 | 565 | def test6(): 566 | cc = CloudClip('~/.config/cloudclip.conf') 567 | cc.list_info() 568 | text = 'now is ' + time.strftime('%Y-%m-%d %H:%M:%S') 569 | print 'uploading: ', text 570 | cc.write_file('test', text) 571 | print 'reading: ', cc.read_file('test') 572 | 573 | def test7(): 574 | args = ['-c', 'happy'] 575 | args = ['-p', 'happy'] 576 | args = ['-p', 'happy'] 577 | # args = ['-l', 'happy'] 578 | args = ['-l', '1234'] 579 | main(sys.argv[:1] + args) 580 | return 0 581 | 582 | # test7() 583 | sys.exit(main()) 584 | 585 | 586 | 587 | 588 | # vim: set ts=4 sw=4 tw=0 noet : 589 | 590 | 591 | --------------------------------------------------------------------------------