chore: make flake8 happy
[grml-paste.git] / grml-paste
1 #!/usr/bin/python3
2 # Filename:      grml-paste
3 # Purpose:       XmlRpc interface client to paste.grml.org
4 # Author:        Michael Gebetsroither <gebi@grml.org>
5 # License:       This file is licensed under the GPL v2.
6 ################################################################################
7
8 import sys
9 from xmlrpc.client import ServerProxy
10 import optparse
11 import inspect
12 import getpass
13
14 # program defaults
15 DEFAULT_SERVER = 'http://paste.grml.org/server.pl'
16
17
18 class ActionFailedException(Exception):
19     '''Thrown if server returned an error'''
20
21     def __init__(self, errormsg, ret):
22         Exception.__init__(self, errormsg, ret)
23
24     def what(self):
25         '''Get errormessage'''
26         return self.args[0]
27
28     def dwhat(self):
29         '''Get more verbose errormessage'''
30         return self.args[1]
31
32
33 class Action(object):
34     def __init__(self, args, opts):
35         self.args_ = args
36         self.opts_ = opts
37
38     def _createProxy(self):
39         return ServerProxy(self.opts_.server, verbose=False)
40
41     def _callProxy(self, functor, server=None):
42         '''Wrapper for xml-rpc calls to server which throws an
43            ActionFailedException on error'''
44         if server is None:
45             server = self._createProxy()
46         ret = functor(server)
47         if ret['rc'] != 0:
48             raise ActionFailedException(ret['statusmessage'], ret)
49         return ret
50
51     def call(self, method_name):
52         '''External Interface to call the appropriate action'''
53         return self.__getattribute__(method_name)()
54
55     def actionAddPaste(self):
56         '''Add paste to the server: <1.line> <2.line> ...
57
58         default     Read paste from stdin.
59         [text]      Every argument on the commandline will be interpreted as
60                     a seperate line of paste.
61         '''
62         server = self._createProxy()
63         o = self.opts_
64         code = self.args_
65         if len(self.args_) == 0:
66             code = [i.rstrip() for i in sys.stdin.readlines()]
67         code = '\n'.join(code)
68         result = self._callProxy(lambda s: s.paste.addPaste(code, o.name, o.expire * 3600, o.lang, o.private),
69                                  server)
70         return result['statusmessage'], result
71
72     def actionDelPaste(self):
73         '''Delete paste from server: <digest>
74
75         <digest>    Digest of paste you want to remove.
76         '''
77         digest = self.args_.pop(0)
78         result = self._callProxy(lambda s: s.paste.deletePaste(digest))
79         return result['statusmessage'], result
80
81     def actionGetPaste(self):
82         '''Get paste from server: <id>
83
84         <id>        Id of paste you want to receive.
85         '''
86         paste_id = self.args_.pop(0)
87         result = self._callProxy(lambda s: s.paste.getPaste(paste_id))
88         return result['code'], result
89
90     def actionGetLangs(self):
91         '''Get supported language highlighting types from server'''
92         result = self._callProxy(lambda s: s.paste.getLanguages())
93         return '\n'.join(result['langs']), result
94
95     def actionAddShortUrl(self):
96         '''Add short-URL: <url>
97
98         <url>        Short-URL to add
99         '''
100         url = self.args_.pop(0)
101         result = self._callProxy(lambda s: s.paste.addShortURL(url))
102         return result['url'], result
103
104     def actionGetShortUrl(self):
105         '''Resolve short-URL: <url>
106
107         <url>        Short-URL to get clicks of
108         '''
109         url = self.args_.pop(0)
110         result = self._callProxy(lambda s: s.paste.resolveShortURL(url))
111         return (result['url'], result)
112
113     def actionGetShortUrlClicks(self):
114         '''Get clicks of short-URL: <url>
115
116         <url>        Short-URL to get clicks of
117         '''
118         url = self.args_.pop(0)
119         result = self._callProxy(lambda s: s.paste.ShortURLClicks(url))
120         return result['count'], result
121
122     def actionHelp(self):
123         '''Print more verbose help about specific action: <action>
124
125         <action>    Topic on which you need more verbose help.
126         '''
127         if len(self.args_) < 1:
128             alias = "help"
129         else:
130             alias = self.args_.pop(0)
131
132         if alias in actions:
133             fun = actions[alias]
134             print(inspect.getdoc(self.__getattribute__(fun)))
135             print("\naliase: " + " ".join([i for i in actions_r[fun] if i != alias]))
136         else:
137             print("Error: No such command - %s" % (alias))
138             OPT_PARSER.print_usage()
139         sys.exit(0)
140
141
142 # actionAddPaste -> [add, a]
143 actions_r = {}
144
145 # add -> actionAddPaste
146 # a   -> actionAddPaste
147 actions = {}
148
149 # option parser
150 OPT_PARSER = None
151
152
153 ##
154 # MAIN
155 ##
156 if __name__ == "__main__":
157     action_spec = ['actionAddPaste add a',
158                    'actionDelPaste del d rm',
159                    'actionGetPaste get g',
160                    'actionGetLangs getlangs gl langs l',
161                    'actionAddShortUrl addurl',
162                    'actionGetShortUrl geturl',
163                    'actionGetShortUrlClicks getclicks',
164                    'actionHelp     help']
165     for i in action_spec:
166         aliases = i.split()
167         cmd = aliases.pop(0)
168         actions_r[cmd] = aliases
169     for (k, v) in actions_r.items():
170         for i in v:
171             actions[i] = k
172
173     usage = "usage: %prog [options] ACTION <args>\n\n" +\
174             "actions:\n" +\
175             "\n".join(["%12s\t%s" % (v[0], inspect.getdoc(getattr(Action, k)).split('\n')[0])
176                       for (k, v) in actions_r.items()])
177     running_user = getpass.getuser()
178     parser = optparse.OptionParser(usage=usage)
179     parser.add_option('-n', '--name', default=running_user, help="Name of poster")
180     parser.add_option('-e', '--expire', type=int, default=72, metavar='HOURS',
181                       help='Time at wich paste should expire')
182     parser.add_option('-l', '--lang', default='Plain', help='Type of language to highlight')
183     parser.add_option("-p", "--private", action="count", dest="private", default=0,
184                       help='Create hidden paste'),
185     parser.add_option('-s', '--server', default=DEFAULT_SERVER,
186                       help='Paste server')
187     parser.add_option('-v', '--verbose', action='count', default=0, help='More output')
188     (opts, args) = parser.parse_args()
189     OPT_PARSER = parser
190
191     if len(args) == 0:
192         parser.error('Please provide me with an action')
193     elif args[0] in actions:
194         cmd = args.pop(0)
195         action = Action(args, opts)
196         try:
197             (msg, ret) = action.call(actions[cmd])
198             if opts.verbose == 0:
199                 print(msg)
200             else:
201                 print(ret)
202         except ActionFailedException as e:
203             sys.stderr.write('Server Error: %s\n' % e.what())
204             if opts.verbose > 0:
205                 print(e.dwhat())
206             sys.exit(1)
207     else:
208         parser.error('Unknown action: %s' % args[0])