@learnpack/learnpack 5.0.262 → 5.0.265

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -330,9 +330,18 @@ class ServeCommand extends SessionCommand_1.default {
330
330
  app.post("/webhooks/:courseSlug/initial-readme-processor", async (req, res) => {
331
331
  const { courseSlug } = req.params;
332
332
  const body = req.body;
333
- // Save the file as courses/courseSlug/README.md
334
- const filePath = `courses/${courseSlug}/README${(0, creatorUtilities_1.getReadmeExtension)(body.parsed.language_code)}`;
335
- await uploadFileToBucket(bucket, body.parsed.content, filePath);
333
+ console.log("RECEIVING INITIAL README WEBHOOK", body);
334
+ const langCode = body.parsed.language_code || body.parsed.output_language_code || "";
335
+ const content = body.parsed.content || body.parsed.translation || "";
336
+ if (!content || !langCode) {
337
+ console.log("No content or language code to save", body);
338
+ return res.status(400).json({
339
+ status: "ERROR",
340
+ message: "No content to save",
341
+ });
342
+ }
343
+ const filePath = `courses/${courseSlug}/README${(0, creatorUtilities_1.getReadmeExtension)(langCode)}`;
344
+ await uploadFileToBucket(bucket, content, filePath);
336
345
  res.json({ status: "SUCCESS" });
337
346
  });
338
347
  app.post("/webhooks/:courseSlug/images/:imageId", async (req, res) => {
@@ -438,6 +447,15 @@ class ServeCommand extends SessionCommand_1.default {
438
447
  await uploadFileToBucket(bucket, JSON.stringify(newSyllabus), `courses/${courseSlug}/.learn/initialSyllabus.json`);
439
448
  res.json({ status: "SUCCESS" });
440
449
  });
450
+ // The following endpoint is used to store an incoming translation where it supposed to be
451
+ app.post("/webhooks/:courseSlug/:exSlug/save-translation", async (req, res) => {
452
+ const { courseSlug, exSlug } = req.params;
453
+ const body = req.body;
454
+ console.log("RECEIVING TRANSLATION WEBHOOK", body);
455
+ const readmePath = `courses/${courseSlug}/exercises/${exSlug}/README${(0, creatorUtilities_1.getReadmeExtension)(body.parsed.output_language_code)}`;
456
+ await uploadFileToBucket(bucket, body.parsed.translation, readmePath);
457
+ res.json({ status: "SUCCESS" });
458
+ });
441
459
  app.get("/check-preview-image/:slug", async (req, res) => {
442
460
  const { slug } = req.params;
443
461
  const file = bucket.file(`courses/${slug}/preview.png`);
@@ -623,65 +641,79 @@ class ServeCommand extends SessionCommand_1.default {
623
641
  return res.status(400).json({ error: "RigoToken not found" });
624
642
  }
625
643
  const languagesToTranslate = languages.split(",");
626
- const course = await bucket
627
- .file(`courses/${courseSlug}/learn.json`)
628
- .download();
629
- const courseJson = JSON.parse(course.toString());
630
- const languageCodes = new Set();
644
+ const languageCodesRes = await (0, rigoActions_1.getLanguageCodes)(rigoToken, {
645
+ raw_languages: languagesToTranslate.join(","),
646
+ });
647
+ const languageCodes = languageCodesRes.parsed.language_codes;
631
648
  try {
632
649
  await Promise.all(exerciseSlugs.map(async (slug) => {
633
650
  const readmePath = `courses/${courseSlug}/exercises/${slug}/README${(0, creatorUtilities_1.getReadmeExtension)(currentLanguage)}`;
634
651
  const readme = await bucket.file(readmePath).download();
635
- await Promise.all(languagesToTranslate.map(async (language) => {
636
- const response = await (0, rigoActions_1.translateExercise)(rigoToken, {
652
+ await Promise.all(languageCodes.map(async (language) => {
653
+ // verify if the translation already exists
654
+ const translationPath = `courses/${courseSlug}/exercises/${slug}/README${(0, creatorUtilities_1.getReadmeExtension)(language)}`;
655
+ const [exists] = await bucket.file(translationPath).exists();
656
+ if (exists) {
657
+ console.log(`Translation in ${language} already exists for exercise ${slug}`);
658
+ return;
659
+ }
660
+ await (0, rigoActions_1.translateExercise)(rigoToken, {
637
661
  text_to_translate: readme.toString(),
638
662
  output_language: language,
639
- exercise_slug: slug,
640
- });
641
- const translatedReadme = await bucket.file(`courses/${courseSlug}/exercises/${slug}/README${(0, creatorUtilities_1.getReadmeExtension)(response.parsed.output_language_code)}`);
642
- await translatedReadme.save(response.parsed.translation);
643
- languageCodes.add(response.parsed.output_language_code);
663
+ }, `${process.env.HOST}/webhooks/${courseSlug}/${slug}/save-translation`);
644
664
  }));
645
665
  }));
646
- const currentLanguages = Object.keys(courseJson.title);
647
- for (const languageCode of currentLanguages) {
648
- if (languageCodes.has(languageCode)) {
649
- languageCodes.delete(languageCode);
666
+ const course = await bucket
667
+ .file(`courses/${courseSlug}/learn.json`)
668
+ .download();
669
+ const courseJson = JSON.parse(course.toString());
670
+ const neededLanguages = new Set();
671
+ let currentLanguages = Object.keys(courseJson.title);
672
+ for (const languageCode of languageCodes) {
673
+ if (!currentLanguages.includes(languageCode)) {
674
+ neededLanguages.add(languageCode);
650
675
  }
651
676
  }
652
- if ([...languageCodes].length > 0) {
653
- const translatedCourseMetadata = await Promise.all([...languageCodes].map(async (languageCode) => {
654
- const result = await (0, rigoActions_1.translateCourseMetadata)(rigoToken, {
655
- title: courseJson.title[currentLanguage],
656
- description: courseJson.description[currentLanguage],
657
- destination_lang_code: languageCode,
658
- });
659
- return {
660
- languageCode,
661
- title: result.parsed.title,
662
- description: result.parsed.description,
663
- };
664
- }));
665
- for (const metadata of translatedCourseMetadata) {
666
- courseJson.title[metadata.languageCode] = metadata.title;
667
- courseJson.description[metadata.languageCode] =
668
- metadata.description;
669
- }
677
+ const neededLanguagesList = [...neededLanguages]
678
+ .map((lang) => lang.toLowerCase())
679
+ .sort();
680
+ if (neededLanguagesList.length > 0) {
681
+ const result = await (0, rigoActions_1.translateCourseMetadata)(rigoToken, {
682
+ title: JSON.stringify(courseJson.title),
683
+ description: JSON.stringify(courseJson.description),
684
+ new_languages: neededLanguagesList.join(","),
685
+ });
686
+ const translateTitle = JSON.parse(result.parsed.title);
687
+ const translateDescription = JSON.parse(result.parsed.description);
688
+ courseJson.title = translateTitle;
689
+ courseJson.description = translateDescription;
670
690
  await uploadFileToBucket(bucket, JSON.stringify(courseJson), `courses/${courseSlug}/learn.json`);
671
- const previewReadme = await bucket.file(`courses/${courseSlug}/README${(0, creatorUtilities_1.getReadmeExtension)(currentLanguage)}`);
672
- const [previewReadmeContent] = await previewReadme.download();
673
- const previewReadmeContentString = previewReadmeContent.toString();
674
- await Promise.all([...languageCodes].map(async (languageCode) => {
675
- const translatedPreviewReadme = await (0, rigoActions_1.translateExercise)(rigoToken, {
676
- text_to_translate: previewReadmeContentString,
677
- output_language: languageCode,
678
- exercise_slug: "preview-readme",
679
- });
680
- await bucket
681
- .file(`courses/${courseSlug}/README${(0, creatorUtilities_1.getReadmeExtension)(languageCode)}`)
682
- .save(translatedPreviewReadme.parsed.translation);
683
- }));
691
+ currentLanguages = Object.keys(courseJson.title);
692
+ }
693
+ // Check that all the READMEs exists
694
+ const missingReadmeTranslations = [];
695
+ let firstAvailable = "";
696
+ for (const languageCode of currentLanguages) {
697
+ // eslint-disable-next-line no-await-in-loop
698
+ const previewReadme = await bucket.file(`courses/${courseSlug}/README${(0, creatorUtilities_1.getReadmeExtension)(languageCode)}`);
699
+ // eslint-disable-next-line no-await-in-loop
700
+ const [exists] = await previewReadme.exists();
701
+ if (!exists) {
702
+ missingReadmeTranslations.push(languageCode);
703
+ }
704
+ else {
705
+ // eslint-disable-next-line no-await-in-loop
706
+ const [previewReadmeContent] = await previewReadme.download();
707
+ const previewReadmeContentString = previewReadmeContent.toString();
708
+ firstAvailable = previewReadmeContentString;
709
+ }
684
710
  }
711
+ await Promise.all(missingReadmeTranslations.map(async (languageCode) => {
712
+ await (0, rigoActions_1.translateExercise)(rigoToken, {
713
+ text_to_translate: firstAvailable,
714
+ output_language: languageCode,
715
+ }, `${process.env.HOST}/webhooks/${courseSlug}/initial-readme-processor`);
716
+ }));
685
717
  return res.status(200).json({ message: "Translated exercises" });
686
718
  }
687
719
  catch (error) {
@@ -78,8 +78,7 @@ class BuildCommand extends SessionCommand_1.default {
78
78
  const response = await (0, rigoActions_1.translateExercise)(rigoToken, {
79
79
  text_to_translate: readme,
80
80
  output_language: language,
81
- exercise_slug: exercise,
82
- });
81
+ }, `${process.env.HOST}/webhooks/translate-exercise`);
83
82
  await (0, creatorUtilities_1.saveTranslatedReadme)(exercise, response.parsed.output_language_code, response.parsed.translation);
84
83
  console_1.default.success(`Translated ${exercise} to ${language} successfully`);
85
84
  }));
@@ -305,8 +305,7 @@ async function default_1(app, configObject, configManager) {
305
305
  const response = await (0, rigoActions_1.translateExercise)(rigoToken, {
306
306
  text_to_translate: readme.body,
307
307
  output_language: language,
308
- exercise_slug: slug,
309
- });
308
+ }, `${process.env.HOST}/webhooks/translate-exercise`);
310
309
  await (0, creatorUtilities_1.saveTranslatedReadme)(slug, response.parsed.output_language_code, response.parsed.translation);
311
310
  (0, sidebarGenerator_1.addExerciseToSidebar)(slug, response.parsed.output_language_code, response.parsed.translated_slug, configDirPath || "");
312
311
  console_1.default.success(`Translated ${slug} to ${language} successfully`);
@@ -19,9 +19,8 @@ export declare function downloadImage(imageUrl: string, savePath: string): Promi
19
19
  type TTranslateInputs = {
20
20
  text_to_translate: string;
21
21
  output_language: string;
22
- exercise_slug: string;
23
22
  };
24
- export declare const translateExercise: (token: string, inputs: TTranslateInputs) => Promise<any>;
23
+ export declare const translateExercise: (token: string, inputs: TTranslateInputs, webhookUrl: string) => Promise<any>;
25
24
  type TGenerateCourseIntroductionInputs = {
26
25
  course_title: string;
27
26
  lessons_context: string;
@@ -63,7 +62,7 @@ export declare const createStructuredPreviewReadme: (token: string, inputs: TCre
63
62
  export declare const translateCourseMetadata: (token: string, inputs: {
64
63
  title: string;
65
64
  description: string;
66
- destination_lang_code: string;
65
+ new_languages: string;
67
66
  }) => Promise<any>;
68
67
  export declare function createPreviewReadme(tutorialDir: string, packageInfo: PackageInfo, rigoToken: string, readmeContents: string[]): Promise<void>;
69
68
  type TReduceReadmeInputs = {
@@ -88,4 +87,8 @@ export declare const isPackageAuthor: (token: string, packageSlug: string) => Pr
88
87
  isAuthor: boolean;
89
88
  status: number;
90
89
  }>;
90
+ type TGetLanguageCodesInputs = {
91
+ raw_languages: string;
92
+ };
93
+ export declare const getLanguageCodes: (token: string, inputs: TGetLanguageCodesInputs) => Promise<any>;
91
94
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isPackageAuthor = exports.fillSidebarJSON = exports.generateCourseShortName = exports.isValidRigoToken = exports.translateCourseMetadata = exports.createStructuredPreviewReadme = exports.readmeCreator = exports.createCodingReadme = exports.createCodeFile = exports.interactiveCreation = exports.generateCourseIntroduction = exports.translateExercise = exports.generateImage = exports.hasCreatorPermission = exports.createReadme = void 0;
3
+ exports.getLanguageCodes = exports.isPackageAuthor = exports.fillSidebarJSON = exports.generateCourseShortName = exports.isValidRigoToken = exports.translateCourseMetadata = exports.createStructuredPreviewReadme = exports.readmeCreator = exports.createCodingReadme = exports.createCodeFile = exports.interactiveCreation = exports.generateCourseIntroduction = exports.translateExercise = exports.generateImage = exports.hasCreatorPermission = exports.createReadme = void 0;
4
4
  exports.downloadImage = downloadImage;
5
5
  exports.createPreviewReadme = createPreviewReadme;
6
6
  exports.makeReadmeReadable = makeReadmeReadable;
@@ -79,11 +79,12 @@ async function downloadImage(imageUrl, savePath) {
79
79
  const buffer = Buffer.from(response.data, "binary");
80
80
  await (0, promises_1.writeFile)(savePath, buffer);
81
81
  }
82
- const translateExercise = async (token, inputs) => {
83
- const response = await axios_1.default.post(`${api_1.RIGOBOT_HOST}/v1/prompting/completion/159/`, {
82
+ const translateExercise = async (token, inputs, webhookUrl) => {
83
+ const response = await axios_1.default.post(`${api_1.RIGOBOT_HOST}/v1/prompting/completion/translate-asset-markdown/`, {
84
84
  inputs: inputs,
85
85
  include_purpose_objective: false,
86
- execute_async: false,
86
+ execute_async: true,
87
+ webhook_url: webhookUrl,
87
88
  }, {
88
89
  headers: {
89
90
  "Content-Type": "application/json",
@@ -272,3 +273,13 @@ const isPackageAuthor = async (token, packageSlug) => {
272
273
  }
273
274
  };
274
275
  exports.isPackageAuthor = isPackageAuthor;
276
+ const getLanguageCodes = async (token, inputs) => {
277
+ const response = await axios_1.default.post(`${api_1.RIGOBOT_HOST}/v1/prompting/completion/get-language-codes/`, { inputs, include_purpose_objective: false, execute_async: false }, {
278
+ headers: {
279
+ "Content-Type": "application/json",
280
+ Authorization: "Token " + token,
281
+ },
282
+ });
283
+ return response.data;
284
+ };
285
+ exports.getLanguageCodes = getLanguageCodes;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@learnpack/learnpack",
3
3
  "description": "Seamlessly build, sell and/or take interactive & auto-graded tutorials, start learning now or build a new tutorial to your audience.",
4
- "version": "5.0.262",
4
+ "version": "5.0.265",
5
5
  "author": "Alejandro Sanchez @alesanchezr",
6
6
  "contributors": [
7
7
  {
@@ -31,6 +31,7 @@ import {
31
31
  isPackageAuthor,
32
32
  createStructuredPreviewReadme,
33
33
  translateCourseMetadata,
34
+ getLanguageCodes,
34
35
  } from "../utils/rigoActions"
35
36
  import * as dotenv from "dotenv"
36
37
  import {
@@ -519,11 +520,25 @@ export default class ServeCommand extends SessionCommand {
519
520
  const { courseSlug } = req.params
520
521
  const body = req.body
521
522
 
522
- // Save the file as courses/courseSlug/README.md
523
+ console.log("RECEIVING INITIAL README WEBHOOK", body)
524
+
525
+ const langCode =
526
+ body.parsed.language_code || body.parsed.output_language_code || ""
527
+
528
+ const content = body.parsed.content || body.parsed.translation || ""
529
+
530
+ if (!content || !langCode) {
531
+ console.log("No content or language code to save", body)
532
+ return res.status(400).json({
533
+ status: "ERROR",
534
+ message: "No content to save",
535
+ })
536
+ }
537
+
523
538
  const filePath = `courses/${courseSlug}/README${getReadmeExtension(
524
- body.parsed.language_code
539
+ langCode
525
540
  )}`
526
- await uploadFileToBucket(bucket, body.parsed.content, filePath)
541
+ await uploadFileToBucket(bucket, content, filePath)
527
542
 
528
543
  res.json({ status: "SUCCESS" })
529
544
  }
@@ -707,6 +722,25 @@ export default class ServeCommand extends SessionCommand {
707
722
  }
708
723
  )
709
724
 
725
+ // The following endpoint is used to store an incoming translation where it supposed to be
726
+ app.post(
727
+ "/webhooks/:courseSlug/:exSlug/save-translation",
728
+ async (req, res) => {
729
+ const { courseSlug, exSlug } = req.params
730
+ const body = req.body
731
+
732
+ console.log("RECEIVING TRANSLATION WEBHOOK", body)
733
+
734
+ const readmePath = `courses/${courseSlug}/exercises/${exSlug}/README${getReadmeExtension(
735
+ body.parsed.output_language_code
736
+ )}`
737
+
738
+ await uploadFileToBucket(bucket, body.parsed.translation, readmePath)
739
+
740
+ res.json({ status: "SUCCESS" })
741
+ }
742
+ )
743
+
710
744
  app.get("/check-preview-image/:slug", async (req, res) => {
711
745
  const { slug } = req.params
712
746
  const file = bucket.file(`courses/${slug}/preview.png`)
@@ -948,13 +982,10 @@ export default class ServeCommand extends SessionCommand {
948
982
  }
949
983
 
950
984
  const languagesToTranslate: string[] = languages.split(",")
951
-
952
- const course = await bucket
953
- .file(`courses/${courseSlug}/learn.json`)
954
- .download()
955
-
956
- const courseJson = JSON.parse(course.toString())
957
- const languageCodes = new Set()
985
+ const languageCodesRes = await getLanguageCodes(rigoToken, {
986
+ raw_languages: languagesToTranslate.join(","),
987
+ })
988
+ const languageCodes = languageCodesRes.parsed.language_codes
958
989
 
959
990
  try {
960
991
  await Promise.all(
@@ -962,59 +993,68 @@ export default class ServeCommand extends SessionCommand {
962
993
  const readmePath = `courses/${courseSlug}/exercises/${slug}/README${getReadmeExtension(
963
994
  currentLanguage
964
995
  )}`
965
-
966
996
  const readme = await bucket.file(readmePath).download()
967
997
 
968
998
  await Promise.all(
969
- languagesToTranslate.map(async (language: string) => {
970
- const response = await translateExercise(rigoToken, {
971
- text_to_translate: readme.toString(),
972
- output_language: language,
973
- exercise_slug: slug,
974
- })
975
-
976
- const translatedReadme = await bucket.file(
977
- `courses/${courseSlug}/exercises/${slug}/README${getReadmeExtension(
978
- response.parsed.output_language_code
979
- )}`
999
+ languageCodes.map(async (language: string) => {
1000
+ // verify if the translation already exists
1001
+ const translationPath = `courses/${courseSlug}/exercises/${slug}/README${getReadmeExtension(
1002
+ language
1003
+ )}`
1004
+
1005
+ const [exists] = await bucket.file(translationPath).exists()
1006
+ if (exists) {
1007
+ console.log(
1008
+ `Translation in ${language} already exists for exercise ${slug}`
1009
+ )
1010
+ return
1011
+ }
1012
+
1013
+ await translateExercise(
1014
+ rigoToken,
1015
+ {
1016
+ text_to_translate: readme.toString(),
1017
+ output_language: language,
1018
+ },
1019
+ `${process.env.HOST}/webhooks/${courseSlug}/${slug}/save-translation`
980
1020
  )
981
- await translatedReadme.save(response.parsed.translation)
982
-
983
- languageCodes.add(response.parsed.output_language_code)
984
1021
  })
985
1022
  )
986
1023
  })
987
1024
  )
988
1025
 
989
- const currentLanguages = Object.keys(courseJson.title)
990
- for (const languageCode of currentLanguages) {
991
- if (languageCodes.has(languageCode)) {
992
- languageCodes.delete(languageCode)
1026
+ const course = await bucket
1027
+ .file(`courses/${courseSlug}/learn.json`)
1028
+ .download()
1029
+
1030
+ const courseJson = JSON.parse(course.toString())
1031
+
1032
+ const neededLanguages = new Set<string>()
1033
+
1034
+ let currentLanguages = Object.keys(courseJson.title)
1035
+
1036
+ for (const languageCode of languageCodes) {
1037
+ if (!currentLanguages.includes(languageCode)) {
1038
+ neededLanguages.add(languageCode)
993
1039
  }
994
1040
  }
995
1041
 
996
- if ([...languageCodes].length > 0) {
997
- const translatedCourseMetadata = await Promise.all(
998
- [...languageCodes].map(async languageCode => {
999
- const result = await translateCourseMetadata(rigoToken, {
1000
- title: courseJson.title[currentLanguage],
1001
- description: courseJson.description[currentLanguage],
1002
- destination_lang_code: languageCode as string,
1003
- })
1042
+ const neededLanguagesList: string[] = [...neededLanguages]
1043
+ .map((lang: string) => lang.toLowerCase())
1044
+ .sort()
1004
1045
 
1005
- return {
1006
- languageCode,
1007
- title: result.parsed.title,
1008
- description: result.parsed.description,
1009
- }
1010
- })
1011
- )
1046
+ if (neededLanguagesList.length > 0) {
1047
+ const result = await translateCourseMetadata(rigoToken, {
1048
+ title: JSON.stringify(courseJson.title),
1049
+ description: JSON.stringify(courseJson.description),
1050
+ new_languages: neededLanguagesList.join(","),
1051
+ })
1012
1052
 
1013
- for (const metadata of translatedCourseMetadata) {
1014
- courseJson.title[metadata.languageCode as string] = metadata.title
1015
- courseJson.description[metadata.languageCode as string] =
1016
- metadata.description
1017
- }
1053
+ const translateTitle = JSON.parse(result.parsed.title)
1054
+ const translateDescription = JSON.parse(result.parsed.description)
1055
+
1056
+ courseJson.title = translateTitle
1057
+ courseJson.description = translateDescription
1018
1058
 
1019
1059
  await uploadFileToBucket(
1020
1060
  bucket,
@@ -1022,34 +1062,45 @@ export default class ServeCommand extends SessionCommand {
1022
1062
  `courses/${courseSlug}/learn.json`
1023
1063
  )
1024
1064
 
1065
+ currentLanguages = Object.keys(courseJson.title)
1066
+ }
1067
+
1068
+ // Check that all the READMEs exists
1069
+
1070
+ const missingReadmeTranslations = []
1071
+ let firstAvailable = ""
1072
+ for (const languageCode of currentLanguages) {
1073
+ // eslint-disable-next-line no-await-in-loop
1025
1074
  const previewReadme = await bucket.file(
1026
- `courses/${courseSlug}/README${getReadmeExtension(currentLanguage)}`
1075
+ `courses/${courseSlug}/README${getReadmeExtension(languageCode)}`
1027
1076
  )
1028
- const [previewReadmeContent] = await previewReadme.download()
1029
- const previewReadmeContentString = previewReadmeContent.toString()
1030
-
1031
- await Promise.all(
1032
- [...languageCodes].map(async languageCode => {
1033
- const translatedPreviewReadme = await translateExercise(
1034
- rigoToken,
1035
- {
1036
- text_to_translate: previewReadmeContentString,
1037
- output_language: languageCode as string,
1038
- exercise_slug: "preview-readme",
1039
- }
1040
- )
1041
1077
 
1042
- await bucket
1043
- .file(
1044
- `courses/${courseSlug}/README${getReadmeExtension(
1045
- languageCode as string
1046
- )}`
1047
- )
1048
- .save(translatedPreviewReadme.parsed.translation)
1049
- })
1050
- )
1078
+ // eslint-disable-next-line no-await-in-loop
1079
+ const [exists] = await previewReadme.exists()
1080
+ if (!exists) {
1081
+ missingReadmeTranslations.push(languageCode)
1082
+ } else {
1083
+ // eslint-disable-next-line no-await-in-loop
1084
+ const [previewReadmeContent] = await previewReadme.download()
1085
+ const previewReadmeContentString = previewReadmeContent.toString()
1086
+ firstAvailable = previewReadmeContentString
1087
+ }
1051
1088
  }
1052
1089
 
1090
+ await Promise.all(
1091
+ missingReadmeTranslations.map(async languageCode => {
1092
+
1093
+ await translateExercise(
1094
+ rigoToken,
1095
+ {
1096
+ text_to_translate: firstAvailable,
1097
+ output_language: languageCode as string,
1098
+ },
1099
+ `${process.env.HOST}/webhooks/${courseSlug}/initial-readme-processor`
1100
+ )
1101
+ })
1102
+ )
1103
+
1053
1104
  return res.status(200).json({ message: "Translated exercises" })
1054
1105
  } catch (error) {
1055
1106
  console.log(error, "ERROR")
@@ -96,11 +96,14 @@ export default class BuildCommand extends SessionCommand {
96
96
  .split(",")
97
97
  .map(async (language: string) => {
98
98
  const readme = await getReadmeForExercise(exercise)
99
- const response = await translateExercise(rigoToken, {
100
- text_to_translate: readme,
101
- output_language: language,
102
- exercise_slug: exercise,
103
- })
99
+ const response = await translateExercise(
100
+ rigoToken,
101
+ {
102
+ text_to_translate: readme,
103
+ output_language: language,
104
+ },
105
+ `${process.env.HOST}/webhooks/translate-exercise`
106
+ )
104
107
 
105
108
  await saveTranslatedReadme(
106
109
  exercise,
@@ -204,8 +204,7 @@ export default async function (
204
204
  // symbolic link to maintain path compatiblity
205
205
  const fetchStaticAsset = withHandler((req, res) => {
206
206
  const filePath = `${config?.dirPath}/assets/${req.params.filePath}`
207
- if (!fs.existsSync(filePath))
208
- throw new Error("File not found: " + filePath)
207
+ if (!fs.existsSync(filePath)) throw new Error("File not found: " + filePath)
209
208
  const content = fs.readFileSync(filePath)
210
209
  res.write(content)
211
210
  res.end()
@@ -436,11 +435,14 @@ throw new Error("File not found: " + filePath)
436
435
 
437
436
  await Promise.all(
438
437
  languagesToTranslate.map(async (language: string) => {
439
- const response = await translateExercise(rigoToken, {
440
- text_to_translate: readme.body,
441
- output_language: language,
442
- exercise_slug: slug,
443
- })
438
+ const response = await translateExercise(
439
+ rigoToken,
440
+ {
441
+ text_to_translate: readme.body,
442
+ output_language: language,
443
+ },
444
+ `${process.env.HOST}/webhooks/translate-exercise`
445
+ )
444
446
 
445
447
  await saveTranslatedReadme(
446
448
  slug,