write-tweet.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. import { Component } from "@angular/core";
  2. import {
  3. IonicPage,
  4. NavController,
  5. NavParams,
  6. LoadingController,
  7. AlertController
  8. } from "ionic-angular";
  9. import {
  10. FormBuilder,
  11. Validators,
  12. FormGroup,
  13. ValidatorFn,
  14. AbstractControl
  15. } from "@angular/forms";
  16. import { TwitterApiProvider } from "../../providers/twitter-api/twitter-api";
  17. import { Storage } from "@ionic/storage";
  18. import { P2pStorageIpfsProvider } from "../../providers/p2p-storage-ipfs/p2p-storage-ipfs";
  19. import { P2pDatabaseGunProvider } from "../../providers/p2p-database-gun/p2p-database-gun";
  20. import twittertext from "twitter-text";
  21. import { CryptoProvider } from "../../providers/crypto/crypto";
  22. import * as openpgp from 'openpgp';
  23. @IonicPage()
  24. @Component({
  25. selector: "page-write-tweet",
  26. templateUrl: "write-tweet.html"
  27. })
  28. export class WriteTweetPage {
  29. tweet: FormGroup;
  30. retweetId: string;
  31. replyToStatusId: string;
  32. retweet;
  33. replyTweet;
  34. openpgp;
  35. privateKey;
  36. publicKey;
  37. pk: any[]=[];
  38. passp = 'super long and hard to guess secret' ;
  39. hkp = new openpgp.HKP('https://sks-keyservers.net/');
  40. constructor(
  41. public navCtrl: NavController,
  42. public navParams: NavParams,
  43. private formBuilder: FormBuilder,
  44. private twitter: TwitterApiProvider,
  45. private loadingCtrl: LoadingController,
  46. private storage: Storage,
  47. private ipfs: P2pStorageIpfsProvider,
  48. private gun: P2pDatabaseGunProvider,
  49. private cryptoUtils: CryptoProvider,
  50. private alertCtrl: AlertController
  51. ) {
  52. this.retweetId = this.navParams.get("tweetId");
  53. this.replyToStatusId = this.navParams.get("replyToStatus");
  54. this.tweet = this.formBuilder.group({
  55. text: [""],
  56. p2p: [false]
  57. });
  58. this.addValidators();
  59. // this.encryptDecryptFunction();
  60. }
  61. // public async encryptDecryptFunction () {
  62. // await openpgp.initWorker({path:'assets/scripts/openpgp.worker.js'});
  63. // let a = await this.generateKeys();
  64. // console.log('a is:',a.publicKeyArmored);
  65. // let b = await this.generateKeys();
  66. // console.log('b is:',b.publicKeyArmored);
  67. // let c = await this.generateKeys();
  68. // this.privateKey =c.privateKeyArmored;
  69. // this.publicKey = c.publicKeyArmored;
  70. // this.pk.push(a.publicKeyArmored);
  71. // this.pk.push(b.publicKeyArmored);
  72. // // this.pk = [`----BEGIN PGP PUBLIC KEY BLOCK-----
  73. // // Version: OpenPGP.js v4.7.1
  74. // // Comment: https://openpgpjs.org
  75. // // xjMEXfAn1xYJKwYBBAHaRw8BAQdAAMVNOABw8MBtrtYR8KC3tSro3wITyApT
  76. // // TVjKVCppD+DNG0pvbiBTbWl0aCA8am9uQGV4YW1wbGUuY29tPsJ3BBAWCgAf
  77. // // BQJd8CfXBgsJBwgDAgQVCAoCAxYCAQIZAQIbAwIeAQAKCRD+efBRXzuMsfA7
  78. // // AQCEgoToFzv2hT9BREdiQp531/AHSyoZWmWvSZSvmga40gD8C+zwbCySnkhQ
  79. // // pb4L0DCKtSDa7pLg2g0OcxJlbSZWHQ3OOARd8CfXEgorBgEEAZdVAQUBAQdA
  80. // // p4mVY17dPWf6VCBqW10Ybk5JgUO6FK0OsETWw3gG2zcDAQgHwmEEGBYIAAkF
  81. // // Al3wJ9cCGwwACgkQ/nnwUV87jLFHbAD9GyoL7dcTDGQoqtrhKozdgnzfugTb
  82. // // er0bwU15WNMjefkA/jEqK9YUNcRrFKIuac9PVibGgutL8ak7ukysw6iTcCsM
  83. // // =fmhE
  84. // // -----END PGP PUBLIC KEY BLOCK-----`,
  85. // // `-----BEGIN PGP PUBLIC KEY BLOCK-----
  86. // // Version: OpenPGP.js v4.7.1
  87. // // Comment: https://openpgpjs.org
  88. // // xjMEXfAn1hYJKwYBBAHaRw8BAQdAsF1ivpd0HU8ogj02LDv6BTOxNMWGZaEc
  89. // // OyZBwqoYJPrNG0pvbiBTbWl0aCA8am9uQGV4YW1wbGUuY29tPsJ3BBAWCgAf
  90. // // BQJd8CfWBgsJBwgDAgQVCAoCAxYCAQIZAQIbAwIeAQAKCRDEruv77flRJ32B
  91. // // AP93GIBcUW2okROoZZhdPVeqjRD72Ft64imXpdZ0jx4ohgEA5Kv9vs2kV73q
  92. // // k6fcdf7qD/i5gMExU0+vV05c9VxBYwfOOARd8CfWEgorBgEEAZdVAQUBAQdA
  93. // // 1J7E03ZopUnsIeNzeiZvba6qxhhUbpmBZ1aN1HhWUlEDAQgHwmEEGBYIAAkF
  94. // // Al3wJ9YCGwwACgkQxK7r++35USdTqQD/ZEg8X5tMx75nQe4mGlyiRjmmtWLw
  95. // // n9bslTdjBIszs/EA/R1WIm6ji4Ru1dJWc3ISisz78xTM2H8U7fnP8yjFcWcD
  96. // // =hgnW
  97. // // -----END PGP PUBLIC KEY BLOCK-----`];
  98. // console.log('array of pub keys is :',this.pk);
  99. // this.pk = this.pk.map(async (key) => {
  100. // return (await openpgp.key.readArmored(key)).keys[0]
  101. // });
  102. // console.log('priv key: ',this.privateKey,'this.pubkey',this.pk);
  103. // let encrypted;
  104. // const privKeyObj = (await openpgp.key.readArmored(this.privateKey)).keys[0];
  105. // console.log('privKeyObj',privKeyObj);
  106. // const bla = await privKeyObj.decrypt(this.passp);
  107. // // const options = {
  108. // // message: openpgp.message.fromText('Hello, World!'), // input as Message object
  109. // // publicKeys: (await openpgp.key.readArmored(this.publicKey)).keys, // for encryption
  110. // // privateKeys: [privKeyObj] // for signing (optional)
  111. // // }
  112. // const options = {
  113. // message: openpgp.message.fromText('Hello, World!'), // input as Message object
  114. // publicKeys: await Promise.all(this.pk), // for encryption
  115. // privateKeys: [privKeyObj] // for signing (optional)
  116. // }
  117. // const ciphertext = await openpgp.encrypt(options);
  118. // encrypted = ciphertext.data; // '-----BEGIN PGP MESSAGE ... END PGP MESSAGE-----'
  119. // console.log('encrypted is:',encrypted);
  120. // let aprivKeyObj = (await openpgp.key.readArmored(a.privateKeyArmored)).keys[0];
  121. // await aprivKeyObj.decrypt(this.passp);
  122. // const options2 = {
  123. // message: await openpgp.message.readArmored(encrypted), // parse armored message
  124. // privateKeys: [aprivKeyObj] // for decryption
  125. // }
  126. // console.log('options2 is: ',options2);
  127. // let plaintext = await openpgp.decrypt(options2);
  128. // console.log('decrypted text is:',plaintext,plaintext.data);
  129. // return plaintext.data // 'Hello, World!'
  130. // }
  131. // public async generateKeys(){
  132. // let options = {
  133. // userIds: [{ name:'Jon Smith', email:'jon@example.com' }], // multiple user IDs
  134. // curve: "ed25519", // ECC curve name
  135. // passphrase: this.passp // protects the private key
  136. // };
  137. // let a = await openpgp.generateKey(options);
  138. // return a;
  139. // // console.log('resolved a = ',a);
  140. // // this.privateKey =a.privateKeyArmored;
  141. // // this.publicKey = a.publicKeyArmored;
  142. // // this.encryptDecryptFunction();
  143. // }
  144. private async addValidators() {
  145. const triggerWords = await this.storage.get("keywords");
  146. const validators = [
  147. Validators.maxLength(140),
  148. this.containsTriggerWord(triggerWords)
  149. ];
  150. this.tweet.controls["text"].setValidators(validators);
  151. }
  152. private containsTriggerWord(triggerWords: string): ValidatorFn {
  153. return (control: AbstractControl): { [key: string]: any } | null => {
  154. if (triggerWords) {
  155. const regexList = triggerWords
  156. .toLowerCase()
  157. .split(", ")
  158. .join("|");
  159. const regex = new RegExp(regexList);
  160. const containsTriggerWord = regex.test(control.value.toLowerCase());
  161. return containsTriggerWord
  162. ? { containsTriggerWord: { value: control.value } }
  163. : null;
  164. } else {
  165. return null;
  166. }
  167. };
  168. }
  169. async ionViewDidLoad() {
  170. if (this.retweetId) {
  171. this.retweet = await this.twitter.fetchTweet(this.retweetId);
  172. }
  173. if (this.replyToStatusId) {
  174. this.replyTweet = await this.twitter.fetchTweet(this.replyToStatusId);
  175. }
  176. }
  177. get tweetCharProgress() {
  178. const progress = 1 - this.tweet.value["text"].length / 140;
  179. const radius = 8;
  180. const circumference = Math.PI * radius * 2;
  181. return progress * circumference;
  182. }
  183. get showTrigger(): boolean {
  184. return (
  185. this.tweet &&
  186. this.tweet.controls &&
  187. this.tweet.controls.text &&
  188. this.tweet.controls.text.errors &&
  189. this.tweet.controls.text.errors["containsTriggerWord"] &&
  190. !this.tweet.controls.p2p.value
  191. );
  192. }
  193. showTriggerInfo() {
  194. this.alertCtrl
  195. .create({
  196. title: "Watch Out!",
  197. message:
  198. "Your tweet contains words you have previously defined to only share securely via P2P. Currently P2P mode is not selected.",
  199. buttons: ["OK"]
  200. })
  201. .present();
  202. }
  203. async submitTweet() {
  204. console.log('Submitting tweet')
  205. const loading = this.loadingCtrl.create();
  206. loading.present();
  207. if (this.tweet.value.p2p) {
  208. loading.setContent("Validate keys...");
  209. if (
  210. (await this.cryptoUtils.isPrivateKeySet()) &&
  211. (await this.cryptoUtils.isPublicKeyPublished())
  212. ) {
  213. loading.setContent("Publish private tweet...");
  214. await this.tweetPrivate();
  215. } else {
  216. loading.dismiss();
  217. const alert = this.alertCtrl.create({
  218. title: "Oooops...",
  219. message:
  220. "Please verify that you have set a private and public key in the settings and that your latest public key was published."
  221. });
  222. alert.present();
  223. return;
  224. }
  225. } else {
  226. loading.setContent("Publish on Twitter...");
  227. await this.twitter.tweet(
  228. this.tweet.value["text"],
  229. this.retweet,
  230. this.replyToStatusId
  231. );
  232. }
  233. loading.dismiss();
  234. this.navCtrl.pop();
  235. }
  236. public async lookupKeys(email:string){
  237. var options = {
  238. query: email
  239. };
  240. let armoredPubkey = await this.hkp.lookup(options);
  241. console.log('armord pubkey',armoredPubkey);
  242. let pubkey = (await openpgp.key.readArmored(armoredPubkey));
  243. console.log('array of opubkes returened from server',pubkey);
  244. pubkey = (await openpgp.key.readArmored(armoredPubkey)).keys[0];
  245. console.log('latest pubkey is:',pubkey);
  246. // console.log('Found public key:',pubkey);
  247. this.pk.push(pubkey);
  248. }
  249. private async tweetPrivate() {
  250. const tweet = await this.buildPrivateTweet();
  251. console.log('tweet is:',tweet.full_text);
  252. const privateKey = await this.storage.get("privateKey");
  253. //fetch followers and their public keys
  254. //assuming the email id of rohit.shiva.gowda
  255. await this.lookupKeys("rohit.hosn@gmail.com");
  256. console.log('array of pub keys is :', this.pk);
  257. this.pk = this.pk.map(async (key) => {
  258. console.log('key is:',key);
  259. return (await openpgp.key.readArmored(key)).keys[0]
  260. });
  261. console.log("after mapping", this.pk);
  262. const options = {
  263. message: openpgp.message.fromText(tweet), // input as Message object
  264. publicKeys: await Promise.all(this.pk), // for encryption
  265. // privateKeys: [privKeyObj] // for signing (optional)
  266. }
  267. const ciphertext = await openpgp.encrypt(options);
  268. const encryptedTweet = ciphertext.data;
  269. console.log('encrypted tweet is:',encryptedTweet);
  270. // const encryptedTweet = this.cryptoUtils.encrypt(
  271. // JSON.stringify(tweet),
  272. // privateKey
  273. // );
  274. const res = await this.ipfs.storeTweet(encryptedTweet);
  275. this.gun.storeLastTweetHashForUser(
  276. tweet.user_id,
  277. res["Hash"],
  278. tweet.created_at
  279. );
  280. this.gun.publishHashtags(tweet.entities.hashtags);
  281. }
  282. private async buildPrivateTweet() {
  283. const status = this.tweet.value["text"].trim();
  284. const entities = await this.getEntities(status);
  285. return {
  286. full_text: status,
  287. user_id: await this.storage.get("userId"),
  288. created_at: Date.now(),
  289. private_tweet: true,
  290. in_reply_to_status_id: this.replyToStatusId,
  291. quoted_status_id: this.retweetId,
  292. display_text_range: [0, status.length],
  293. entities: entities
  294. };
  295. }
  296. private async getEntities(status: string) {
  297. return {
  298. hashtags: twittertext.extractHashtagsWithIndices(status),
  299. urls: twittertext.extractUrlsWithIndices(status),
  300. user_mentions: await this.getMentions(status)
  301. };
  302. }
  303. private async getMentions(status: string) {
  304. // extract mentions
  305. const entities = twittertext.extractMentionsWithIndices(status);
  306. // add user_id
  307. const entitiesWithPromises = entities.map(async mention => {
  308. try {
  309. const user = await this.twitter.fetchUserFromScreenName(
  310. mention.screenName
  311. );
  312. mention["id_str"] = user[0]["id_str"];
  313. mention["screen_name"] = mention.screenName;
  314. delete mention.screenName;
  315. } catch (err) {
  316. console.error(
  317. "There is no user signed up to twitter with username: " +
  318. mention.screenName
  319. );
  320. }
  321. return mention;
  322. });
  323. // filter for valid users and return
  324. return (await Promise.all(entitiesWithPromises)).filter(el =>
  325. el.hasOwnProperty("id_str")
  326. );
  327. }
  328. }