write-tweet.ts 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  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 { PgpKeyServerProvider } from "../../providers/pgp-key-server/pgp-key-server";
  21. import twittertext from "twitter-text";
  22. import { CryptoProvider } from "../../providers/crypto/crypto";
  23. import * as openpgp from 'openpgp';
  24. @IonicPage()
  25. @Component({
  26. selector: "page-write-tweet",
  27. templateUrl: "write-tweet.html"
  28. })
  29. export class WriteTweetPage {
  30. tweet: FormGroup;
  31. retweetId: string;
  32. replyToStatusId: string;
  33. retweet;
  34. replyTweet;
  35. openpgp;
  36. privateKey;
  37. publicKey;
  38. pk: any[] = [];
  39. passp = 'super long and hard to guess secret';
  40. hkp = new openpgp.HKP('https://sks-keyservers.net/');
  41. constructor(
  42. public navCtrl: NavController,
  43. public navParams: NavParams,
  44. private formBuilder: FormBuilder,
  45. private twitter: TwitterApiProvider,
  46. private loadingCtrl: LoadingController,
  47. private storage: Storage,
  48. private ipfs: P2pStorageIpfsProvider,
  49. private gun: P2pDatabaseGunProvider,
  50. private cryptoUtils: CryptoProvider,
  51. private opnpgp: PgpKeyServerProvider,
  52. private alertCtrl: AlertController
  53. ) {
  54. this.retweetId = this.navParams.get("tweetId");
  55. this.replyToStatusId = this.navParams.get("replyToStatus");
  56. this.tweet = this.formBuilder.group({
  57. text: [""],
  58. p2p: [false]
  59. });
  60. this.addValidators();
  61. // this.testPvtKeySaveFlow();
  62. }
  63. private async testPvtKeySaveFlow(){
  64. console.log("saving pvt keys");
  65. let a = await this.storage.get("privateKey");
  66. const privKeyObj = (await openpgp.key.readArmored(a)).keys[0];
  67. //publish pvt key
  68. // await this.cryptoUtils.publishPrivateKey(privKeyObj);
  69. //retireve private key from Gun
  70. let user = await this.storage.get("userId");
  71. let pvtKey = await this.gun.getPvtKeyHistory(user);
  72. console.log("pvr key received is:",pvtKey.key);
  73. //fetch data from ipfs link
  74. let keyHistory = await this.ipfs.fetchJson(pvtKey.key);
  75. console.log("Key history is:",keyHistory);
  76. //decrypt the pvt key with passphrase
  77. // await privKeyObj.decrypt(this.passphrase);
  78. }
  79. private async addValidators() {
  80. const triggerWords = await this.storage.get("keywords");
  81. const validators = [
  82. Validators.maxLength(140),
  83. this.containsTriggerWord(triggerWords)
  84. ];
  85. this.tweet.controls["text"].setValidators(validators);
  86. }
  87. private containsTriggerWord(triggerWords: string): ValidatorFn {
  88. return (control: AbstractControl): {
  89. [key: string]: any
  90. } | null => {
  91. if (triggerWords) {
  92. const regexList = triggerWords
  93. .toLowerCase()
  94. .split(", ")
  95. .join("|");
  96. const regex = new RegExp(regexList);
  97. const containsTriggerWord = regex.test(control.value.toLowerCase());
  98. return containsTriggerWord ? { containsTriggerWord: { value: control.value } } :
  99. null;
  100. } else {
  101. return null;
  102. }
  103. };
  104. }
  105. async ionViewDidLoad() {
  106. if (this.retweetId) {
  107. this.retweet = await this.twitter.fetchTweet(this.retweetId);
  108. }
  109. if (this.replyToStatusId) {
  110. this.replyTweet = await this.twitter.fetchTweet(this.replyToStatusId);
  111. }
  112. }
  113. get tweetCharProgress() {
  114. const progress = 1 - this.tweet.value["text"].length / 140;
  115. const radius = 8;
  116. const circumference = Math.PI * radius * 2;
  117. return progress * circumference;
  118. }
  119. get showTrigger(): boolean {
  120. return (
  121. this.tweet &&
  122. this.tweet.controls &&
  123. this.tweet.controls.text &&
  124. this.tweet.controls.text.errors &&
  125. this.tweet.controls.text.errors["containsTriggerWord"] &&
  126. !this.tweet.controls.p2p.value
  127. );
  128. }
  129. showTriggerInfo() {
  130. this.alertCtrl
  131. .create({
  132. title: "Watch Out!",
  133. message: "Your tweet contains words you have previously defined to only share securely via P2P. Currently P2P mode is not selected.",
  134. buttons: ["OK"]
  135. })
  136. .present();
  137. }
  138. async submitTweet() {
  139. const loading = this.loadingCtrl.create();
  140. loading.present();
  141. if (this.tweet.value.p2p) {
  142. loading.setContent("Validate keys...");
  143. if (
  144. (await this.cryptoUtils.isPrivateKeySet()) &&
  145. (await this.cryptoUtils.isPublicKeyPublished())
  146. ) {
  147. loading.setContent("Publish private tweet...");
  148. let result = await this.tweetPrivate();
  149. } else {
  150. loading.dismiss();
  151. const alert = this.alertCtrl.create({
  152. title: "Oooops...",
  153. message: "Please verify that you have set a private and public key in the settings and that your latest public key was published."
  154. });
  155. alert.present();
  156. return;
  157. }
  158. } else {
  159. loading.setContent("Publish on Twitter...");
  160. await this.twitter.tweet(
  161. this.tweet.value["text"],
  162. this.retweet,
  163. this.replyToStatusId
  164. );
  165. }
  166. loading.dismiss();
  167. this.navCtrl.pop();
  168. }
  169. private async tweetPrivate() {
  170. const tweet = await this.buildPrivateTweet();
  171. const privateKey = await this.storage.get("privateKey");
  172. //fetch followers and their public keys
  173. //encrypting for self
  174. let email = await this.storage.get("email");
  175. await this.opnpgp.lookupKeys(email);
  176. //encrypt the tweet with multiple keys
  177. let encryptedTweet = await this.opnpgp.encrypt(JSON.stringify(tweet));
  178. this.storeIPFS(encryptedTweet, tweet)
  179. return encryptedTweet;
  180. }
  181. private async storeIPFS(result, tweet){
  182. const res = await this.ipfs.storeTweet(result);
  183. this.gun.storeLastTweetHashForUser(
  184. tweet.user_id,
  185. res["Hash"],
  186. tweet.created_at
  187. );
  188. this.gun.publishHashtags(tweet.entities.hashtags);
  189. }
  190. private async buildPrivateTweet() {
  191. const status = this.tweet.value["text"].trim();
  192. const entities = await this.getEntities(status);
  193. return {
  194. full_text: status,
  195. user_id: await this.storage.get("userId"),
  196. created_at: Date.now(),
  197. private_tweet: true,
  198. in_reply_to_status_id: this.replyToStatusId,
  199. quoted_status_id: this.retweetId,
  200. display_text_range: [0, status.length],
  201. entities: entities
  202. };
  203. }
  204. private async getEntities(status: string) {
  205. return {
  206. hashtags: twittertext.extractHashtagsWithIndices(status),
  207. urls: twittertext.extractUrlsWithIndices(status),
  208. user_mentions: await this.getMentions(status)
  209. };
  210. }
  211. private async getMentions(status: string) {
  212. // extract mentions
  213. const entities = twittertext.extractMentionsWithIndices(status);
  214. // add user_id
  215. const entitiesWithPromises = entities.map(async mention => {
  216. try {
  217. const user = await this.twitter.fetchUserFromScreenName(
  218. mention.screenName
  219. );
  220. mention["id_str"] = user[0]["id_str"];
  221. mention["screen_name"] = mention.screenName;
  222. delete mention.screenName;
  223. } catch (err) {
  224. console.error(
  225. "There is no user signed up to twitter with username: " +
  226. mention.screenName
  227. );
  228. }
  229. return mention;
  230. });
  231. // filter for valid users and return
  232. return (await Promise.all(entitiesWithPromises)).filter(el =>
  233. el.hasOwnProperty("id_str")
  234. );
  235. }
  236. }