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