Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# (c) Stefan Countryman, 2019 

2 

3""" 

4Send messages or upload files to Slack using LLAMA's built-in methods. 

5""" 

6 

7import os 

8from sys import stdin 

9import logging 

10from argparse import Namespace 

11from llama.cli import get_logging_cli, CliParser 

12from llama.com.slack import ( 

13 SLACK_TOKENS, 

14 SLACK_CHANNELS, 

15 send_message, 

16 get_users, 

17 tag_users, 

18 client, 

19) 

20 

21LOGGER = logging.getLogger(__name__) 

22USER_ROW_FMT = "{id:<15} {display_name:<23} {real_name:<39}" 

23USER_HEADER = USER_ROW_FMT.format(id="ID", display_name="DISPLAY-NAME", 

24 real_name="REAL-NAME") 

25 

26 

27def postprocess_print_channels(_self: CliParser, namespace: Namespace): 

28 """Print available channels for organizations specified in 

29 ``namespace.organization`` if ``namespace.print_channels`` is ``True`` and 

30 then exit. 

31 """ 

32 if not namespace.print_channels: 

33 return 

34 print(f"CHANNELS AVAILABLE FOR {namespace.organization}:") 

35 for chan in SLACK_CHANNELS[namespace.organization]: 

36 print(f"- {chan}") 

37 exit() 

38 

39 

40def postprocess_print_users(self: CliParser, namespace: Namespace): 

41 """Print users who can be tagged for organizations specified in 

42 ``namespace.organization`` if ``namespace.print_users`` is ``True`` and 

43 then exit. 

44 """ 

45 if not namespace.print_users: 

46 return 

47 users = [u for u in get_users(namespace.organization)['members'] 

48 if not u['deleted']] 

49 print(f"TAGGABLE USERS AVAILABLE FOR {namespace.organization}\n", 

50 "Use ID, DISPLAY-NAME, or REAL-NAME when specifying users.\n", 

51 USER_HEADER, '='*len(USER_HEADER), sep='\n') 

52 for user in users: 

53 print(USER_ROW_FMT.format(id=user['id'], 

54 display_name=user['profile']['display_name'], 

55 real_name=user['profile']['real_name'])) 

56 exit() 

57 

58 

59def postprocess_check_slack_token(self: CliParser, namespace: Namespace): 

60 """Make sure we have a slack token for the specified 

61 ``namespace.organization``; if not, error out.""" 

62 if SLACK_TOKENS[namespace.organization] is None: 

63 self.error("Must specify a slack token for organization using " 

64 f"environmental variables for {namespace.organization}." 

65 "To do this, run `export " 

66 f"SLACK_API_TOKEN_{namespace.organization.upper()}=" 

67 "<your-token>`.") 

68 

69 

70def get_parser(): 

71 """Get CLI Parser.""" 

72 parser = CliParser(description=__doc__, 

73 parents=(get_logging_cli('/dev/null', 'info'),)) 

74 parser.POSTPROCESSORS += ( 

75 postprocess_print_channels, 

76 postprocess_check_slack_token, 

77 postprocess_print_users, 

78 ) 

79 parser.add_argument("channels", metavar='channel', nargs="*", help=""" 

80 Which Slack channel to upload to.""") 

81 parser.add_argument("-o", "--organization", choices=tuple(SLACK_CHANNELS), 

82 default="LLAMA", help=""" 

83 Which oranization to send a message to. (default: LLAMA)""") 

84 parser.add_argument("-m", "--message", help=""" 

85 Text content to send. Can be formatted using Slack's markdown-esque 

86 syntax. If not specified, read message from a pipe or send with no 

87 message if no pipe is being used.""") 

88 parser.add_argument("-u", "--users", default=[], nargs="+", help=""" 

89 Names of users to tag. You can use real names, display names, or Slack 

90 user IDs. They will be alerted to your message if they have access to 

91 the channel you are posting to.""") 

92 parser.add_argument("-f", "--file", help=""" 

93 Path to a file to upload as part of the message.""") 

94 parser.add_argument("--print-users", action='store_true', help=""" 

95 Print a list of taggable users for the specified organization and then 

96 exit.""") 

97 parser.add_argument("--print-channels", action='store_true', help=""" 

98 Print a list of channels that can be posted to for the specified 

99 organization and then exit.""") 

100 return parser 

101 

102 

103def main(): 

104 """Run CLI.""" 

105 parser = get_parser() 

106 args = parser.parse_args() 

107 if args.message is None: 

108 if not stdin.isatty(): 

109 msg = stdin.read() 

110 else: 

111 msg = '' 

112 else: 

113 msg = args.message 

114 if args.users: 

115 msg = tag_users(args.organization, args.users, recover=False) + msg 

116 for chan in args.channels: 

117 if args.file: 

118 client(SLACK_TOKENS[args.organization]).files_upload( 

119 channels=chan, 

120 filename=os.path.basename(args.file), 

121 file=args.file, 

122 initial_comment=msg, 

123 ) 

124 else: 

125 send_message( 

126 organization=args.organization, 

127 message=msg, 

128 channel=chan, 

129 recover=False 

130 )