Crossfire Server, Trunk
dialog_check.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 # dialog_check.py
3 # This script is *not* intended to be used by the crossfire plugin, it is
4 # designed to verify the correctness of a dialog script -independantly of the crossfire server.
5 # Typically you will want to run this script against a single file, the one that you specify in
6 # the event_say, but if you want to check all of the .msg files in the map distribution, then
7 # you can run something like:
8 # for i in $(grep -h name.*msg ../../ -r | cut -d " " -f 2 | sort | uniq); do echo $i && python dialog_check.py "../../"$i; done
9 # from maps/python/dialog
10 
11 import cjson
12 import sys
13 import os
14 import re
15 
16 # There is an upper limit on the maximum message length the server can cope with, exceeding it throws out a warning.
17 MAX_MSG_LENGTH = 2048
18 
19 def checkactionfile(filename, condition):
20  args = condition[1:]
21  checkstatus = 0
22  if not os.path.isfile(filename):
23  print("Error: No script to support action: ", condition[0], "Expected: ", filename)
24  return False
25  else:
26  actf = open(filename,"r")
27  argnum = 0
28  for actline in actf.readlines():
29  if checkstatus == 0:
30  if actline.find("## DIALOGCHECK") != -1:
31  checkstatus = 1
32  elif checkstatus == 1:
33  if actline.find("## ENDDIALOGCHECK") != -1:
34  checkstatus = 2
35  elif actline.find("## MINARGS") != -1:
36  num = actline.split()[2]
37  if not num.isdigit():
38  print("ERROR: Action definition for script ", filename, " MINARGS not defined")
39  return False
40  else:
41  if len(args)<int(num):
42  print("ERROR: Insufficiant options passed to script ", filename, "expected ", int(num), " recieved ", len(args))
43  return False
44  elif actline.find("## MAXARGS") != -1:
45  num = actline.split()[2]
46  if not num.isdigit():
47  print("ERROR: Action definition for script ", filename, " MAXARGS not defined")
48  return False
49  elif int(num) == 0:
50  # zero means there is no upper limit to the number of arguments
51  pass
52  else:
53  if len(args)>int(num):
54  print("ERROR: Too many options passed to script ", filename, "expected ", int(num), " recieved ", len(args))
55  return False
56  elif actline.find("##") != 1:
57  # This is a regexp for one of the arguments
58  if argnum < len(args):
59  argmatch = re.compile(actline.split()[1])
60  if not argmatch.match(args[argnum]):
61  print("ERROR: Argument ", argnum+2, "of rule: ", condition, " doesn't match regexp ", actline.split()[1])
62  return False
63  argnum+=1
64  if checkstatus != 2:
65  print("Warning: No dialogcheck block for file ", filename, " Unable to check condition ", condition)
66  return True
67  return True
68 
69 def checkdialoguefile(msgfile, location):
70 
71  rulenumber = 0
72  errors = 0
73  warnings = 0
74  extrafiles =[]
75  params = {}
76  try:
77  f = open(msgfile,"rb")
78  except:
79  print("ERROR: Can't open file, ", msgfile)
80  errors +=1
81  else:
82  try:
83  params = cjson.decode(f.read())
84  except:
85  print("ERROR: Failed to parse file, ", msgfile, "not a valid json file")
86  errors +=1
87  f.close()
88  if "location" in params:
89  if not location == '':
90  print("Warning: Location defined multiple times in included files")
91  warnings+=1
92  location = params["location"]
93  if location =='':
94  print("Warning: no location was specified")
95  warnings +=1
96  rulenumber =0
97  for jsonRule in params["rules"]:
98  rulenumber +=1
99  include = 0
100  msg=0
101  post=0
102  match=0
103  pre =0
104  for action in jsonRule:
105  if action == "pre":
106  pre+=1
107  for condition in jsonRule["pre"]:
108  action = condition[0]
109  path = os.path.join("pre/", action + ".py")
110  if not checkactionfile(path, condition):
111  print("ERROR: verification of action file ", path, " failed for rule ", rulenumber, " condition ", condition)
112  errors+=1
113 
114  elif action == "msg":
115  for line in jsonRule["msg"]:
116  if len(line) > MAX_MSG_LENGTH:
117  # We won't print out the entire line, because it's very
118  # very long, but we'll print the first 70 characters in order to help identify it
119  print("WARNING: A Dialog Line for rule", rulenumber, "is too long. (", len(line), "characters, maximum is", MAX_MSG_LENGTH, ") \nLine begins:", line[:70])
120  warnings+=1
121  msg+=1
122  elif action == "post":
123  post+=1
124  for condition in jsonRule["post"]:
125  action = condition[0]
126  path = os.path.join("post/", action + ".py")
127  if not checkactionfile(path, condition):
128  print("ERROR: verification of action file ", path, " failed for rule ", rulenumber, " condition ", condition)
129  errors +=1
130 
131  elif action == "match":
132  match+=1
133  elif action == "replies":
134  pass
135  elif action == "comment":
136  pass
137  elif action == "include":
138  include+=1
139  for condition in jsonRule["include"]:
140  if condition[0] == "/":
141  inclname = os.path.join("../..", condition[1:])
142  else:
143  inclname = os.path.join(os.path.dirname(msgfile), condition)
144  extrafiles.append(inclname)
145  else:
146  print("Warning: Ignoring unknown rule:", action)
147  warnings+=1
148  if (include == 1 and msg+post+match == 0) or (msg == 1 and post == 1 and match ==1 and pre == 1):
149  pass
150  else:
151  print("ERROR: Rule created with an invalid combination of actions, actions are: ", list(jsonRule.keys()))
152  errors +=1
153  newfiles =0
154  newrules =0
155  newwarnings=0
156  newerrors=0
157  if len(extrafiles) > 0:
158  for extrapath in extrafiles:
159  newfiles, newrules, newwarnings, newerrors = checkdialoguefile(extrapath, location)
160  print("checked ", newrules, "rules from file", extrapath, "Found ", newerrors, " errors and ", newwarnings,"warnings")
161  warnings +=newwarnings
162  rulenumber+=newrules
163  errors+=newerrors
164  extrafiles = []
165  return (1+newfiles, rulenumber, warnings, errors)
166 
167 if len(sys.argv) < 2:
168  print("usage: python dialog_check.py path/to/dialogfile.msg")
169  exit()
170 for arg in sys.argv[1:]:
171  newfiles, rulecount, newwarnings, newerrors = checkdialoguefile(arg, '')
172  print("checked ", rulecount, "rules from file", arg, "Found ", newerrors, " errors and ", newwarnings,"warnings")
dialog_check.checkactionfile
def checkactionfile(filename, condition)
Definition: dialog_check.py:19
CFBank.open
def open()
Definition: CFBank.py:70
guildoracle.list
list
Definition: guildoracle.py:87
dialog_check.checkdialoguefile
def checkdialoguefile(msgfile, location)
Definition: dialog_check.py:69
make_face_from_files.int
int
Definition: make_face_from_files.py:32