Compare commits

...

6 Commits

Author SHA1 Message Date
Ruben van de Ven db211e7357 search field in editor 2020-02-23 22:48:16 +03:00
Ruben van de Ven dbafd3d80d Check for missing conditions like in moscow 2020-02-23 21:11:34 +03:00
Ruben van de Ven 3832652dc9 Test higher watermark 2020-02-22 02:02:55 +03:00
Ruben van de Ven 7d23e4a478 Merge branch 'master' of gitlab.com:hugvey/hugvey 2020-02-22 01:42:19 +03:00
Hugvey Central Command c59a07b333 Add socks tunnel description 2020-02-18 14:29:30 +01:00
Hugvey Central Command 2a1ba69a52 Little comment 2020-02-16 14:34:03 +01:00
8 changed files with 156 additions and 10 deletions

View File

@ -273,3 +273,10 @@ alias.url = ("/local/" => "/home/hugvey/hugvey/local/")
``` ```
## When Telegram is blocked
In Russia, Telegram is blocked, to circumvent this, we set it to run over a SOCKS proxy:
`autossh -M 0 -o "ServerAliveInterval 30" -o "ServerAliveCountMax 3" -N -D 9090 rubenvandeven.com`

View File

@ -216,7 +216,7 @@ class VoiceServer(object):
try: try:
address = "tcp://*:{}".format(self.config['voice']['port'] + self.hugvey.id) address = "tcp://*:{}".format(self.config['voice']['port'] + self.hugvey.id)
self.voice_socket = self.ctx.socket(zmq.PUB) self.voice_socket = self.ctx.socket(zmq.PUB)
self.voice_socket.set_hwm(15) self.voice_socket.set_hwm(25) # not too high, otherwise we flood the network after a short interuption (with stuff that is 'old' anyway)
self.voice_socket.bind(address) self.voice_socket.bind(address)
logger.info( logger.info(

View File

@ -1090,6 +1090,7 @@ class Diversion(object):
""" """
(1) last spoken at all (1) last spoken at all
(2) or duration for this last reply only (2) or duration for this last reply only
Only can kick in if there's no 'timeout' condition set.
""" """
if story.currentDiversion: if story.currentDiversion:
return return

View File

@ -121,7 +121,14 @@ class Toolbox:
logger.info("{} files missing".format(len(missingFiles))) logger.info("{} files missing".format(len(missingFiles)))
@classmethod
def find_direction_for_condition(cls, conditionId, story):
for i, item in enumerate(story):
if item['@type'] == 'Direction':
for dConditionId in item['conditions']:
if dConditionId == conditionId:
return item
def fix_story_file(self, lang_code): def fix_story_file(self, lang_code):
if lang_code not in self.languages.keys(): if lang_code not in self.languages.keys():
logger.critical("Invalid langauge code") logger.critical("Invalid langauge code")
@ -136,10 +143,11 @@ class Toolbox:
if len(beginnings) < 1: if len(beginnings) < 1:
logger.critical("No beginning set") logger.critical("No beginning set")
if len(beginnings) > 1: if len(beginnings) > 1:
logger.warn(f"{len(beginnings)} beginning messages configured. Set only one") beginningIds = [i['@id'] for i in beginnings]
logger.warn(f"{len(beginnings)} beginning messages configured. Set only one of {beginningIds}")
itemsPerId = {item['@id']: item for item in story} itemsPerId = {item['@id']: item for item in story}
orphans = 0
for i, item in enumerate(story): for i, item in enumerate(story):
if item['@type'] == 'Direction': if item['@type'] == 'Direction':
if type(item['source']) == dict: if type(item['source']) == dict:
@ -152,11 +160,21 @@ class Toolbox:
logger.info(f"Direction pointed to {item['source']}") logger.info(f"Direction pointed to {item['source']}")
logger.info(f"Will now point to {validMsg}") logger.info(f"Will now point to {validMsg}")
item['source'] = item['source']['@id'] item['source'] = item['source']['@id']
for conditionId in item['conditions']:
if conditionId not in itemsPerId:
logger.critical(f"Direction {item['@id']} refers to non-existing condition {conditionId}! (This will result in a crash when playing the message)")
if item['@type'] == 'Condition': if item['@type'] == 'Condition':
direction = self.find_direction_for_condition(item['@id'], story)
if not direction:
orphans +=1
# This should be fine, but I don't dare to do it yet...
# logger.info("Clear residu condition {item['@id']} ... this is not properly done by the editor.")
# del story[i]
continue
if item['type'] == 'messagePlayed': if item['type'] == 'messagePlayed':
msgId = item['vars']['msgId'].strip() msgId = item['vars']['msgId'].strip()
if msgId not in itemsPerId: if msgId not in itemsPerId:
logger.critical(f"Message played condition for non-existing message {msgId}!") logger.warning(f"Message played condition for non-existing message {msgId} when going from {direction['source']} to {direction['target']}! (this will ignore the condition)")
if item['type'] == 'replyContains': if item['type'] == 'replyContains':
if 'regex' in item['vars'] and len(item['vars']['regex'].rstrip()): if 'regex' in item['vars'] and len(item['vars']['regex'].rstrip()):
try: try:
@ -165,6 +183,8 @@ class Toolbox:
logger.critical(f"Invalid regex for condition {item['@id']}: {item['vars']['regex'].rstrip()}") logger.critical(f"Invalid regex for condition {item['@id']}: {item['vars']['regex'].rstrip()}")
logger.exception(e) logger.exception(e)
logger.debug( f"Can clear {orphans} orphaned conditions (uncomment code in tools.py)")
with open(filename, 'w') as fp: with open(filename, 'w') as fp:
json.dump(story, fp, indent=2) json.dump(story, fp, indent=2)
logger.info(f"Wrote to {filename}") logger.info(f"Wrote to {filename}")
@ -274,4 +294,36 @@ class Toolbox:
json.dump(story, fp, indent=2) json.dump(story, fp, indent=2)
logger.info(f"Wrote to {filename}") logger.info(f"Wrote to {filename}")
def parse_cutelog(self, filename):
with open(filename,'r') as fp:
cutelog = json.load(fp);
hugvey_ids = list(range(1,30))
hugveys_stats = {}
for id in hugvey_ids:
print(f"HUGVEY {id}")
log = [i for i in cutelog if 'name' in i and i['name'].startswith(f'hugvey.{id}.')]
txts = [i for i in log if 'msg' in i and ((i['msg'].startswith('Text: ') and i['msg'] != "Text: ") or i['msg'].startswith('Current message') or i['msg'].startswith('ignore'))]
last = None
for txt in txts:
if last:
if txt['msg'].startswith('Current'):
print('--------------------', txt['created'])
elif txt['msg'].startswith('ignore'):
print('/////////////////////', txt['created'])
else:
print(txt['created'] - last['created'], txt['msg'], txt['levelname']
)
last = txt
else:
last = txt
tC = [i for i in log if 'msg' in i and (i['msg'].startswith("Condition is met"))]
tR = [i for i in log if 'msg' in i and (i['msg'].startswith("Received {'file"))]
tP = [i for i in log if 'msg' in i and (i['msg'].startswith("['play'"))]
for i, txt in enumerate(tP):
print(txt['created']-tC[i]['created'], txt['msg'], tC[i]['msg'], tR[i]['msg'])
print('===================')

View File

@ -40,11 +40,24 @@ if __name__ == '__main__':
metavar=("LANG_CODE", "CVS_FILE"), metavar=("LANG_CODE", "CVS_FILE"),
nargs=2 nargs=2
) )
argParser.add_argument(
'--parse-cutelog',
default=None,
help="Parse the speech of a cutelog logfile",
metavar="CUTELOG STORED FILE",
)
argParser.add_argument(
'--verbose',
'-v',
action='count', default=0
)
args = argParser.parse_args() args = argParser.parse_args()
loglevel = logging.NOTSET if args.verbose > 1 else logging.DEBUG if args.verbose > 0 else logging.INFO
coloredlogs.install( coloredlogs.install(
level=logging.DEBUG level=loglevel
) )
logger = logging.getLogger("toolbox") logger = logging.getLogger("toolbox")
@ -60,4 +73,6 @@ if __name__ == '__main__':
if args.csv: if args.csv:
tools.generate_story_csv(args.csv) tools.generate_story_csv(args.csv)
if args.import_csv: if args.import_csv:
tools.import_story_csv(*args.import_csv) tools.import_story_csv(*args.import_csv)
if args.parse_cutelog:
tools.parse_cutelog(args.parse_cutelog)

1
www/css/vis-timeline-graph2d.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -16,7 +16,7 @@
<div id="interface" class='showStatus'> <div id="interface" class='showStatus'>
<div id='status'> <div id='status'>
<div id='overview'> <div id='overview'>
<span class="loop_time" title="uptime: {{uptime}}" onclick="document.getElementById('time_update').classList.toggle('visible')">{{loop_timer}}</span> <span class="loop_time" :title="['uptime', uptime]" onclick="document.getElementById('time_update').classList.toggle('visible')">{{loop_timer}}</span>
<div id='time_update'> <div id='time_update'>
<input type="text" value="00:00:00" name="loop_time_value" id="loop_time_value" /> <input type="text" value="00:00:00" name="loop_time_value" id="loop_time_value" />
<input type="button" onclick="panopticon.change_loop_time(document.getElementById('loop_time_value').value)" value="set time"> <input type="button" onclick="panopticon.change_loop_time(document.getElementById('loop_time_value').value)" value="set time">
@ -94,6 +94,7 @@
<div id="btn-diversions" class="btn">View Diversions</div> <div id="btn-diversions" class="btn">View Diversions</div>
<div id="btn-audio" class="btn">View Audio Files</div> <div id="btn-audio" class="btn">View Audio Files</div>
<div id="btn-config" class="btn">Configuration</div> <div id="btn-config" class="btn">Configuration</div>
<form id='search'><input type='text' id='search-field'> <input type='submit' id='search-go' value='Search'></form>
</div> </div>
<div id="msg"></div> <div id="msg"></div>
<svg id='graph' viewbox="-640 -512 1280 1024" <svg id='graph' viewbox="-640 -512 1280 1024"

View File

@ -354,6 +354,8 @@ class Graph {
this.directions = []; // initialise empty array. For the simulation, make sure we keep the same array object this.directions = []; // initialise empty array. For the simulation, make sure we keep the same array object
this.conditions = []; // initialise empty array. For the simulation, make sure we keep the same array object this.conditions = []; // initialise empty array. For the simulation, make sure we keep the same array object
this.diversions = []; // initialise empty array. For the simulation, make sure we keep the same array object this.diversions = []; // initialise empty array. For the simulation, make sure we keep the same array object
this.previousSearch = "";
this.searchStep = 0;
let graph = this; let graph = this;
this.controlDown = false; this.controlDown = false;
@ -372,9 +374,11 @@ class Graph {
let c = this.container; let c = this.container;
let zoomed = function() { let zoomed = function() {
console.log(d3.event);
c.attr( "transform", d3.event.transform ); c.attr( "transform", d3.event.transform );
} }
this.svg.call( d3.zoom() this.zoom = d3.zoom();
this.svg.call( this.zoom
.scaleExtent( [1 / 16, 8] ) .scaleExtent( [1 / 16, 8] )
.on( "zoom", zoomed ) ); .on( "zoom", zoomed ) );
@ -398,6 +402,71 @@ class Graph {
document.getElementById( 'btn-diversions' ).addEventListener( 'click', function( e ) { graph.showDiversions(); } ); document.getElementById( 'btn-diversions' ).addEventListener( 'click', function( e ) { graph.showDiversions(); } );
document.getElementById( 'btn-audio' ).addEventListener( 'click', function( e ) { graph.showAudioFiles(); } ); document.getElementById( 'btn-audio' ).addEventListener( 'click', function( e ) { graph.showAudioFiles(); } );
document.getElementById( 'btn-config' ).addEventListener( 'click', function( e ) { graph.showConfiguration(); } ); document.getElementById( 'btn-config' ).addEventListener( 'click', function( e ) { graph.showConfiguration(); } );
document.getElementById( 'search' ).addEventListener( 'submit', function( e ) { e.preventDefault(); graph.searchSubmit(); } );
}
searchSubmit() {
let query = document.getElementById( 'search-field' ).value;
if(query.length < 1) {
console.log('no query');
return;
}
if(query == this.previousSearch) {
this.searchStep++;
} else{
this.searchStep = 0;
}
this.previousSearch = query;
let results = this.findMessages(query);
if(results.length < 1) {
alert("No message found");
return;
}
let ri = this.searchStep % results.length;
let msg = results[ri];
console.log(msg);
this.zoom.scaleTo(this.svg, 1);
this.zoom.translateTo(this.svg, msg.x + 1280/2, msg.y + 1024/2);
this.selectMsg(msg);
document.getElementById("search-go").value = "Search ("+(ri+1) + "/" + results.length +")";
}
findMessages(query) {
let results = [];
for(let msg of this.messages) {
if(msg['@id'] == query || msg['@id'].indexOf(query) > -1) {
results.push(msg);
}
if(msg['text'] == query || (msg.hasOwnProperty('label') && msg['label'] == query)) {
results.push(msg);
}
}
// second loop for priority
for(let msg of this.messages) {
if(msg['text'].indexOf(query) > -1 || (msg.hasOwnProperty('label') && msg['label'].indexOf(query) > -1)) {
results.push(msg);
}
}
return results;
}
findMessage(query, matchNr) {
if(typeof matchNr == 'undefined') {
matchNr = 0;
}
let results = this.findMessages(query)
if(results.length) {
return results[matchNr % results.length];
}
} }
clickMsg( msg ) { clickMsg( msg ) {