· 6 years ago · Sep 16, 2019, 05:30 PM
1package pine;
2
3import java.time.Year;
4import java.util.Calendar;
5import java.util.HashSet;
6import java.util.Map.Entry;
7import java.util.NavigableMap;
8import java.util.Set;
9import java.util.TreeMap;
10
11import javax.crypto.KeyGenerator;
12import javax.crypto.SecretKey;
13
14/**
15 * Generates the String text necessary to generate pinescript code for trial and member secret keys.
16 */
17public class PineKeygen {
18
19 /** Default time zone. */
20 private static final String defaultTZ = "America/New_York";
21
22 /**
23 * Key: Integer order of the key<br>
24 * Value: The secret key
25 */
26 private static NavigableMap<Integer, String> trialKeys = new TreeMap<>();
27
28 /**
29 * Key: Integer order of the key<br>
30 * Value: Key: Integer order of the key, Value = The secret key
31 */
32 private static NavigableMap<Integer, NavigableMap<Integer, String>> subKeys = new TreeMap<>();
33
34 /** Master Key. */
35 private static String masterKey = "";
36
37 /** Calendar. */
38 private static Calendar calendar = Calendar.getInstance();
39
40 /**
41 * @param args Syntax: validVarName:String numMembers:int numProducts:int everyXWeeks:int timeZone:String
42 * endHour:int endMinute:int
43 * <p>
44 * <i>validVarName</i> will have a boolean <tt>true</tt> or <tt>false</tt> if the tested key is
45 * currently valid. A valid key is <tt>true</tt> only if the time stamp associated with that
46 * key (if it exists) has not yet expired for a trial, or it is an active subscription key
47 * (required).
48 * <p>
49 * <i>numProducts</i> is an integer that designates the total number of products to generate
50 * active member keys for (required)
51 * <p>
52 * <i>numMembers</i> is an integer that designates the total number of active member keys to
53 * generate for each product(required)
54 * <p>
55 * <i>everyXDays</i> is an integer that designates how often a key is generated, in days.
56 * <b>7</b> designates that 1 key will be generated every 7 days starting at 23:59 on the first
57 * Sunday of the year (required).
58 * <p>
59 * <i>timeZone</i> optional argument, example = GMT+5
60 * <p>
61 * <i>endHour</i> optional argument, example = 23
62 * <p>
63 * <i>endMinute</i> optional argument, example = 59
64 * <p>
65 */
66 public static void main(String[] args) {
67 try {
68 if (args.length < 4) {
69 throw new IllegalArgumentException("Invalid argument count.");
70 }
71
72 String varName = args[0];
73 varName = varName.equals("validTrial") || varName.equals("validSub") ? varName + "_" : varName;
74
75 int numProducts = Integer.valueOf(args[2]);
76 int dayRate = Integer.valueOf(args[3]);
77
78 Set<String> bytes = new HashSet<>();
79
80 KeyGenerator keygen = KeyGenerator.getInstance("AES");
81 keygen.init(128);
82
83 SecretKey sKey;
84 byte[] enc;
85
86 for (int i = 0; i < 365; i += dayRate) {
87 sKey = keygen.generateKey();
88 enc = sKey.getEncoded();
89
90 StringBuilder sb = new StringBuilder();
91
92 for (int j = 0; j < enc.length; j++) {
93 sb.append(integerToHex(enc[j]));
94 }
95
96 String s = sb.toString();
97
98 while (bytes.contains(s)) {
99 sKey = keygen.generateKey();
100 enc = sKey.getEncoded();
101
102 sb = new StringBuilder();
103
104 for (int j = 0; j < enc.length; j++) {
105 sb.append(integerToHex(enc[j]));
106 }
107
108 s = sb.toString();
109 }
110
111 trialKeys.put(i, s);
112 bytes.add(s);
113 }
114
115 int numSubs = Integer.valueOf(args[1]);
116
117 for (int i = 0; i < numProducts; i++) {
118 NavigableMap<Integer, String> map = new TreeMap<>();
119 subKeys.put(i, map);
120
121 for (int j = 0; j < numSubs; j++) {
122 sKey = keygen.generateKey();
123 enc = sKey.getEncoded();
124
125 StringBuilder sb = new StringBuilder();
126
127 for (int k = 0; k < enc.length; k++) {
128 sb.append(integerToHex(enc[k]));
129 }
130
131 String s = sb.toString();
132
133 while (bytes.contains(s)) {
134 sKey = keygen.generateKey();
135 enc = sKey.getEncoded();
136
137 sb = new StringBuilder();
138
139 for (int k = 0; k < enc.length; k++) {
140 sb.append(integerToHex(enc[k]));
141 }
142
143 s = sb.toString();
144 }
145
146 map.put(j, s);
147 bytes.add(s);
148 }
149 }
150
151 for (int i = 0; i < 1; i++) {
152 sKey = keygen.generateKey();
153 enc = sKey.getEncoded();
154
155 StringBuilder sb = new StringBuilder();
156
157 for (int j = 0; j < enc.length; j++) {
158 sb.append(integerToHex(enc[j]));
159 }
160
161 String s = sb.toString();
162
163 while (bytes.contains(s)) {
164 sKey = keygen.generateKey();
165 enc = sKey.getEncoded();
166
167 sb = new StringBuilder();
168
169 for (int j = 0; j < enc.length; j++) {
170 sb.append(integerToHex(enc[j]));
171 }
172
173 s = sb.toString();
174 }
175
176 masterKey = s;
177 }
178
179 String timezone = "default";
180 int hour = 23;
181 int minute = 59;
182
183 if (args.length > 4) {
184 try {
185 hour = Integer.valueOf(args[4]);
186 } catch (NumberFormatException e) {
187 timezone = args[4];
188 }
189
190 if (args.length > 5) {
191 if (timezone.equals("default")) {
192 minute = Integer.valueOf(args[5]);
193 } else {
194 hour = Integer.valueOf(args[5]);
195 }
196
197 if (args.length > 6) {
198 minute = Integer.valueOf(args[6]);
199 }
200 }
201 }
202
203 timezone = timezone.equals("default") ? defaultTZ : timezone;
204 timezone = timezone.trim().replaceAll(" ", "");
205 hour = Math.max(0, Math.min(23, hour));
206 minute = Math.max(0, Math.min(59, minute));
207
208 StringBuilder sb = new StringBuilder("\n\nkey = input(\"\", title=\"Key\")\n\n")
209 .append("t = timenow\n\n")
210 .append("validTrial").append(" = ");
211
212 StringBuilder sb2 = new StringBuilder("// Trial Keys\n\n");
213
214 setFirstSunday(Year.now().getValue());
215
216 // for trading view, add + 1 to the month,
217
218 for (Entry<Integer, String> e : trialKeys.entrySet()) {
219 String key = e.getValue();
220
221 int year = calendar.get(Calendar.YEAR);
222 int month = calendar.get(Calendar.MONTH) + 1;
223 int day = calendar.get(Calendar.DAY_OF_MONTH);
224
225 calendar.add(Calendar.DATE, dayRate);
226
227 sb.append("(key == \"").append(key).append("\" and ")
228 .append("t <= ").append(timestamp(timezone, year, month, day, hour, minute))
229 .append(") or ");
230
231 sb2.append("// ").append(key).append(" (")
232 .append(formatYearMonthDay(year, month, day, hour, minute)).append(")\n");
233 }
234
235 String out = sb.toString();
236
237 sb = new StringBuilder(out.substring(0, out.length() - 4));
238 int pNum = 1;
239
240 for (Entry<Integer, NavigableMap<Integer, String>> p : subKeys.entrySet()) {
241 sb.append("\n\nvalidSub").append(pNum++).append(" = ");
242
243 for (Entry<Integer, String> e : p.getValue().entrySet()) {
244 String key = e.getValue();
245 sb.append("key == \"").append(key).append("\" or ");
246 }
247
248 sb.append("key == \"").append(masterKey).append("\"");
249 }
250
251 sb.append("\n\n").append(varName).append(" = ").append("validTrial");
252
253 for (int i = 0; i < numProducts; i++) {
254 sb.append(" or validSub").append(i + 1);
255 }
256
257 sb.append("\n\n").append(sb2.toString());
258 pNum = 1;
259
260 for (Entry<Integer, NavigableMap<Integer, String>> p : subKeys.entrySet()) {
261 sb.append("\n// Subscription Keys - ").append(pNum++).append("\n\n");
262
263 for (Entry<Integer, String> e : p.getValue().entrySet()) {
264 sb.append("// ").append(e.getValue()).append("\n");
265 }
266 }
267
268 sb.append("\n// Master Key\n\n").append("// ").append(masterKey).append("\n");
269
270 System.out.println(sb.toString());
271
272 } catch (Exception e) {
273 System.out
274 .println("\nSyntax: expiredVarName:String numMembers:int numProducts:int everyXDays:int timeZone:String endHour:int endMinute:int\n\n"
275 +
276 "validVarName = A boolean true or false if the tested key "
277 +
278 "is currently valid. A valid key is true only if the time stamp "
279 +
280 "associated with that key (if it exists) has not yet expired for a trial, "
281 +
282 "or it is an active subscription key(required)\n\n"
283 +
284 "numMembers = an integer that designates the number of active members subscription "
285 +
286 "keys to generate (required) \n\n"
287 +
288 "numProducts = is an integer that designates the total number of products to generate "
289 +
290 "active member keys for (required) \n\n"
291 +
292 "everyXDays = an integer that designates how often a key is "
293 +
294 "generated, in days. 7 designates that 1 key will be generated every 7 days starting "
295 +
296 "at endHour:endMinute on the first Sunday of the year (required)\n\n"
297 +
298 "timeZone = Designates the timezone, example = GMT+5 or America/New_York (optional)\n\n"
299 +
300 "endHour = Designates the end hour, example = 23 (optional)\n\n"
301 +
302 "endMinute = Designates the end minute, example = 59 (optional)\n\n"
303 +
304 "Full Example\n\n> valid 20 3 14 12 0\n\n will generate a 20 member " +
305 "subscription keys for 3 different products (total of 60 member keys), " +
306 "and a trial key every 14 days starting on Sunday at 12:00 PM America/New_York\n");
307
308 e.printStackTrace();
309 }
310 }
311
312 static void setFirstSunday(int year) {
313 setFirstDayOfWeek(year, Calendar.JANUARY, Calendar.SUNDAY);
314 }
315
316 static void setFirstDayOfWeek(int year, int dayofWeek) {
317 setFirstDayOfWeek(year, Calendar.JANUARY, dayofWeek);
318 }
319
320 static void setFirstDayOfWeek(int year, int month, int dayofweek) {
321 calendar.set(Calendar.MONTH, month);
322 calendar.set(Calendar.YEAR, year);
323
324 int dom = 1;
325 calendar.set(Calendar.DAY_OF_MONTH, dom);
326
327 while (calendar.get(Calendar.DAY_OF_WEEK) != dayofweek) {
328 calendar.set(Calendar.DAY_OF_MONTH, ++dom);
329 }
330 }
331
332 static String timestamp(String tz, int year, int month, int dayofmonth, int hour, int minute) {
333 return new StringBuilder("timestamp(\"")
334 .append(tz).append("\", ")
335 .append(year).append(", ")
336 .append(month).append(", ")
337 .append(dayofmonth).append(", ")
338 .append(hour).append(", ")
339 .append(minute)
340 .append(")").toString();
341
342 }
343
344 static String formatYearMonthDay(int year, int month, int day, int hour, int minute) {
345 String m = (month < 10 ? "0" : "") + String.valueOf(month);
346 String d = (day < 10 ? "0" : "") + String.valueOf(day);
347 String h = (hour < 10 ? "0" : "") + String.valueOf(hour);
348 String min = (minute < 10 ? "0" : "") + String.valueOf(minute);
349
350 return new StringBuilder().append(year).append("-").append(m).append("-").append(d).append("-")
351 .append(h).append(":").append(min)
352 .toString();
353
354 }
355
356 static String integerToHex(int value) {
357 value = value + 128; // convert to unsigned int
358
359 int base = (int) Math.floor(value / 16);
360 int rem = value - base * 16;
361
362 String c1 = base == 10 ? "A" : base == 11 ? "B" : base == 12 ? "C" :
363 base == 13 ? "D" : base == 14 ? "E" : base == 15 ? "F" : String.valueOf(base);
364
365 String c2 = rem == 10 ? "A" : rem == 11 ? "B" : rem == 12 ? "C" :
366 rem == 13 ? "D" : rem == 14 ? "E" : rem == 15 ? "F" : String.valueOf(rem);
367
368 return c1 + c2;
369 }
370}