-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathListener.py
More file actions
executable file
·151 lines (132 loc) · 5.29 KB
/
Copy pathListener.py
File metadata and controls
executable file
·151 lines (132 loc) · 5.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#! /usr/bin/env python3
#
# Listen/read a glider's call in dialog,
# then generate a goto file and send it to the Dockserver
#
# There are thre methods of listening/reading a dialog file:
# Using SFMC 8.5's API interface, output_glider_dialog_data.js
# Reading a file with the API's output
# Reading a log file
#
# July-2020, Pat Welch, pat@mousebrains.com
import argparse
import logging
import re
import json
import subprocess
import MyLogger
from Dialog import Dialog
class Listener:
def __init__(self, args:argparse.ArgumentParser, logger:logging.Logger, dialog:Dialog) -> None:
self.args = args
self.logger = logger
self.dialog = dialog
@staticmethod
def addArgs(parser:argparse.ArgumentParser) -> None:
grp = parser.add_mutually_exclusive_group(required=True)
grp.add_argument("--apiListen", action="store_true",
help="Should the API be used to read the dialog?")
grp.add_argument("--apiInput", type=str, metavar="filename",
help="Name of API file to read")
grp.add_argument("--dialogInput", type=str, action='append', metavar="filename",
help="Name of dialog file to read")
grp = parser.add_argument_group(description="API options")
grp.add_argument("--apiDir", type=str, metavar="dir",
help="Where SFMC's API JavaScripts are located")
grp.add_argument("--apiCopy", type=str, metavar="filename",
help="Write out a copy of what is read from the API")
grp.add_argument("--glider", type=str, required=True, metavar="name",
help="Name of glider to operate on")
def __procLine(self, line:str) -> None:
self.logger.debug("%s", line.strip())
self.dialog.put(line)
def __procBuffer(self, buffer:str, qPartial:bool = False) -> str:
while len(buffer):
index = buffer.find("\n")
if index < 0: # No newline found
if qPartial:
self.__procLine(buffer)
return ""
return buffer
self.__procLine(buffer[0:(index+1)])
buffer = buffer[(index+1):]
return ""
def __apiLine(self, line:bytearray, buffer:str) -> str:
a = re.fullmatch(bytes(r"([{].+[}])\x00\n", "utf-8"), line)
if a is None: return buffer
msg = json.loads(a[1])
if "data" not in msg: return buffer
return self.__procBuffer(buffer + msg["data"], False)
def __apiListen(self) -> bool:
args = self.args
logger = self.logger
if args.apiDir is None:
raise Exception("You must specify --apiDir when using --apiListen")
cmd = (self.args.nodeCommand, "output_glider_dialog_data.js", args.glider)
with subprocess.Popen(cmd,
cwd=args.apiDir,
shell=False,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT) as p:
fp = None if args.apiCopy is None else open(args.apiCopy, "ab")
buffer = ""
while True:
line = p.stdout.readline()
if re.match(b"Error attempting to get glider data", line) is not None:
logger.error("ERROR executing\ncmd=%s\ndir=%s\n%s", cmd, args.apiDir, line)
raise Exception("Error starting API Listener")
if (len(line) == 0) and (p.poll() is not None):
break # Broken connection
if fp is not None:
fp.write(line)
fp.flush()
buffer = self.__apiLine(line, buffer)
if fp is not None: fp.close()
self.__procBuffer(buffer, True)
return True
def __apiInput(self, fn:str) -> bool:
logger = self.logger
logger.info("Opening %s", fn)
with open(fn, "rb") as fp:
buffer= ""
for line in fp:
buffer = self.__apiLine(line, buffer)
self.__procBuffer(buffer, True) # partial line
return False
def __dialogInput(self, fn:str) -> bool:
logger = self.logger
logger.info("Opening %s", fn)
with open(fn, "r") as fp:
buffer = ""
while True:
txt = fp.read(1024)
if len(txt) == 0: # EOF
self.__procBuffer(buffer, True)
return False
buffer = self.__procBuffer(buffer + txt)
return False
def listen(self) -> bool:
args = self.args
if args.apiListen: return self.__apiListen()
if args.apiInput: return self.__apiInput(args.apiInput)
for fn in args.dialogInput:
self.__dialogInput(fn)
return False
parser = argparse.ArgumentParser(description="Glider Dialog Listener")
MyLogger.addArgs(parser)
Listener.addArgs(parser)
Dialog.addArgs(parser)
args = parser.parse_args()
logger = MyLogger.mkLogger(args)
logger.info("args=%s", args)
dialog = Dialog(args, logger)
dialog.start() # Start the update thread
listener = Listener(args, logger, dialog)
try:
while listener.listen():
# Listen to the API interface or read in a file
# Loop if need be for the API interface
pass
except:
logger.exception("Unexpected Exception")
dialog.join() # Wait for all queued messages to be done