איך ליצור בעצמך בוטים וכלים לסטימאיט - מדריך למתחילים - פרק 3

in #hebrewguides7 years ago (edited)

ברוכים הבאים לפרק השלישי בסידרה המרגשת איך ליצור בעצמך בוטים וכלים לסטימאיט.

התירגול נעשה בשפת ג'אווה סקריפט (JS) על בסיס מנוע NodeJS. אם אין לכם מושג על מה מדובר תצטרכו לחזור אל ההדרכה הקודמת כדי להקים לכם סביבת עבודה בסיסית שתלווה אתכם במהלך ההדרכות הבאות. לא נדרש ידע מוקדם בתיכנות, נעבור על הדברים יחד צעד-אחר-צעד.

החל מפרק זה המדריכים ישלבו הסברים תיאורטיים יחד עם תרגול מעשי. חשוב מאוד להצמיד את ההתנסות בפועל עם הלימוד התיאורטי, כי המושגים הם מופשטים ומהר מאוד הם נערמים ומעמיסים על המח עד שאפשר "לאבד את זה" בקלות... הדרך הטובה ביותר להבין איך זה עובד באמת, להרגיש את זה, היא פשוט דרך ניסוי וטעייה. למדתם משהו? נסו בעצמכם, עד שזה ירגיש נכון וטבעי.

התרגול יתבצע על ידי כך שמעתיקים חתיכות קוד מהמדריך לעורך הקוד (NOTEPAD או אחר), שומרים את הקובץ ומריצים את התהליך דרך פקודת node hello.js או כל שם אחר שתתנו לקובץ שלכם.

יש? מתקדמים!

steemit-howto-3.png

האתיקה של הבוטים

בעבודה עם בוטים כדאי להיות זהירים. אתם רשאים להשתמש בבוטים כאוות נפשכם, אבל קחו בחשבון מהי התוצאה שאתם גורמים על ידי כך. בוטים שמצביעים לכל עבר, גם על תכנים גרועים, פוגעים בפלטפורמה ובסופו של דבר ביכולת של כולנו להתקדם, להנות, ולהרוויח. כדאי לחשוב על בוטים כמשהו שעושה בשבילכם משהו שהייתם רוצים לעשות ממילא, וכעת קל יותר כשהוא נעשה באופן אוטומאטי. בדר"כ אנו חושבים על בוט כמשתמש עצמאי בסטימאיט, אבל האמת היא שאנחנו יכולים להפעיל אותו גם דרך המשתמש שלנו. אם נשמור על פרופיל נמוך וניתן לו התנהגות קרובה לשלנו, יהיה קשה להבחין שמדובר בבוט...

הבוט שיצביע על כל תגובה שנקבל לפוסטים שלנו

אנחנו אוהבים תגובות. תגובות מעידות בדר"כ על מעורבות ועניין והן מחזקות את הקשרים בסטימאיט. הגיוני שנרצה לפרגן למגיבים שלנו בUPVOTE לתגובה שלהם. בשביל התרגול נבנה יחד במדריך הזה בוט שיתן הצבעה UPVOTE לכל תגובה שנקבל לפוסטים שלנו. בחרתי ברעיון הזה בשביל הלימוד והתרגול. האם זה בוט נכון לנו? האם הוא 'אתי'? זה כבר עניין שלכם.

תכנון

אל תזלזלו בתכנון! לפעמים יש לנו נטיה לקפוץ מיד למים, כי זה כיף, אבל בלי תכנון מוקדם אפשר להיקלע לסיבוכים אינסופיים ומייאשים. לפני שנכתוב אפילו שורת קוד אחת כדאי לסדר לעצמנו בראש במילים פשוטות, בשפה שלנו
איך הבוט שלנו הולך לעבוד:

  1. הבוט יסרוק את הבלוקים האחרונים בבלוקצ'יין
  2. עבור כל בלוק: הבוט יחפש פעולה של תגובה שנוספה לפוסטים של המשתמש שלנו
  3. עבור כל תגובה כזו: הבלוק ישלח הצבעה לתגובה
  4. הבוט ימשיך להאזין לבלוקצ'יין ולבצע את שלבים 2-3 עבור כל בלוק חדש שמגיע.

שימו לב לפעולה המעגלית של הבוט שמתבטאת בשורה 4. הבוט ימשיך לפעול עד שנכבה אותו בכח או שהמחשב ייכבה מאיזושהי סיבה. כזכור מהפרק הקודם - כדי שבוט יפעל אנחנו צריכים להשאיר את החלון שלו פתוח. אם נרצה לבנות בוט מקצועי מומלץ לאחסן אותו על שרת חיצוני בענן (או אולי בבלוקצ'יין של אחסון בוטים?). אם הבוט לא היה מעגלי הוא היה נכבה מאליו ברגע שהוא היה מסיים את סדר הפעולות שלו.

עכשיו כשהתכנון פרוש לנו מול העיניים קל יותר לממש אותו בקוד, שלב אחרי שלב.

קריאת בלוקים מהבלוקצ'יין

את רשימת הפקודות שנוכל לבצע ביחס לבלוקצ'יין אפשר למצוא בתיעוד של החבילה של סטים עבור NODE
, בפרוייקט שלה בגיטהאב GITHUB (אתר שמארח המון פרוייקטים של קוד פתוח).
היכנסו לתיעוד ולחצו על Globals בתוכן העניינים. שם תמצאו את קטע הקוד שהשתמשנו בו בפרק הקודם כדי להציג כמה פרטי מידע על מצב הבלוקצ'יין כרגע, תחת הכותרת
Get Dynamic Global Properties. מה שאנחנו צריכים משם כרגע זה את המספר של הבלוק האחרון, אז בואו נעתיק את זה שוב:

steem.api.getDynamicGlobalProperties(function(err, result) {
  console.log(err, result);
});

יש לוודא שהקוד שלנו מכיל לפני זה את השורה שמייבאת את החבילה של סטים לאחר שהורדנו אותה באמצעות NPM:

var steem = require('steem');

ועכשיו נריץ:

how3-1.png

הפרט שמעניין אותנו הוא זה שמסומן בריבוע אדום head_block_number, בלוק הראש, העליון כרגע בבלוקצ'יין, זה שנוצר אחרון. בזמן כתיבת מדובר בבלוק מספר 19710391, אבל כל 3 שניות נוסף עוד אחד חדש.

עכשיו אנחנו רוצים לראות את תוכן הבלוק הזה, אז נחזור לתיעוד (אני מקווה שהוא פתוח לכם בחלון מקביל) והפעם נדלג לקטגוריה Blocks and transactions דרך תוכן העניינים. ושם נמצא את Get Block החביב. נעתיק את חתיכת הקוד לעורך שלנו:


steem.api.getBlock(blockNum, function(err, result) {
  console.log(err, result);
});

ופשוט נחליף את הביטוי blockNum עם המספר של הבלוק האחרון שמצאנו.

ונריץ.

הפעם נקבל המון מידע שיהיה לנו קשה לדלות ממנו את מה שצריך (תגובות שניתנות לפוסטים שלנו, נכון?).

אז אין ברירה, אנחנו צריכים ללמוד לפרק את הפלט!

אוכל! קדימה אוכל!

אם יש לכם מעט נסיון בתכנות ברור לכם שמה שעשינו כרגע הוא פרימטיבי להחריד. למה לנו להעתיק באופן ידני את המספר של הבלוק האחרון אם אפשר להגיד לקוד לעשות את זה בעצמו. הסיבה שהדרכתי אתכם לעשות את זה היא כדי להרגיש מה נצרך לעשות פה, וכדי שתקבלו מספיק מוטיבציה לחקור קצת יותר איך גורמים למחשב לעשות זאת בעצמו. אז זה טיפה מורכב, מוכנים?

בואו נבין את הקוד

נתבונן שוב בחתיכת הקוד שמביאה לנו נתונים מהבלוקצ'יין ונבין מה היא עושה:

steem.api.getDynamicGlobalProperties(function(err, result) {
  console.log(err, result);
});

אני עובר מילה מילה.
steem - זו הדרך שלנו לפנות לחבילה שייבאנו לפני כן דרך הפקודה var steem = require('steem');
api - זו מעין חטיבה פנימית בתוך החבילה שכוללת את כל הקריאה למידע מהבלוקצ'יין (לא כוללת את הפעולות שאנו יכולים לבצע). נכון שאמרנו שהכל זה API, אבל האמת היא שבסטים יש הגדרה של API שמאפשר הצגה של נתונים, פוסטים, מצב של משתמש וכו', ויש פעולה שנקראת Broadcast שנגיע אליה בהמשך, שהיא הכנה של טרנזקציה חדשה ושליחתה לבלוקצ'יין על מנת שהיא תיארז בתוך בלוק חדש. למזלנו את כל התהליך של ההכנה של הטרנזקציה חסכו לנו יוצרי החבילה, והיא נעשית מאליה עם קריאה פשוטה לפקודה שמייצרת הצבעה, או מפרסמת פוסט או תגובה וכו'.
getDynamicGlobalProperties - הפקודה הספציפית שמבקשת לקבל את המאפיינים הדינאמיים הגלובלים. מאפיינים - כלומר איזשהם נתונים, דינאמיים - זה בגלל שהם משתנים כל הזמן, וגלובאלים - בגלל שהם נגישים לכל מי שמבקש אותם.
עכשיו יש לנו פתיחת סוגריים, מכיון שהפקודה הזו (שנקראת גם פונקציה, או מתודה) צריכה לקבל איזשהם פרמטרים כדי לפעול. במקרה הזה הפרמטר היחידי הוא מה שנקרא "פונקציית קולבק" Callback function. תיאור הפונקציה הזו היא כל מה שמופיע בתוך הסוגריים:

function(err, result) {
  console.log(err, result);
}

כאן אנחנו צריכים להסביר קצת יותר לעומק, מה זה פונקציית קולבק.

כל קריאה לבלוקצ'יין הוא תהליך שדורש זמן. כאשר אנו רוצים לקבל מידע, אנחנו צריכים לשלוח בקשה לשרת מרוחק ולהמתין לתשובה שלו. זה עשוי לקחת זמן, גם אם זה שנייה או שתיים, זה עדיין זמן. כדי לא לתקוע את מהלך הקוד שלנו בהמתנה לתשובה, פיתחו את הרעיון של "פונקציית קולבק".
תוך כדי שאנו מזמינים את המידע, אנו מספקים גם כן איזושהי הגדרה של מה לעשות עם המידע כאשר הוא מגיע, ההגדרה הזו היא פונקציית הקולבק, כלומר איזושהי מתודה שתיקרא בחזרה (מכאן השם call back) על ידי התהליך כאשר הוא יסתיים.

אם זה היה מסובך, אסביר זאת באמצעות משל שקשור באוכל (איכשהו זה נושא שכולם מתחברים דרכו): הפנייה של הקוד שלנו אל השרת המרוחק היא כמו לשבת במסעדה ולהזמין משהו מהתפריט (התפריט זה התיעוד שהשתמשנו בו...). את ההזמנה אנו נותנים למלצר, והמלצר ניגש לטבח במטבח ומעביר לו את ההזמנה.

עכשיו המלצר יכול להמתין עד שהטבח יסיים את החלק שלו, זה עשוי לקחת זמן, או שהוא יכול להגיד למלצר "אחי, כשאתה מסיים פשוט תקרא לי בחזרה, בסדר?" הטבח אומר "סבבה אח שלו"** ואז המלצר פנוי ללכת לתת שירות לעוד אורחים במסעדה, ולהגדיל את פוטנציאל הטיפים שהוא יקבל מהם.

המלצר הוא תוכנת השרת של סטים (שנקראת steemd) אליה אנו שולחים את הבקשה שלנו. תוכנת השרת הולכת למידע עצמו של הבלוקצ'יין ומוציאה ממנו מה שצריך.
במקרה שלנו, מי שמייצר את הקולבק זה אנחנו, ולא המלצר. כדי שלא נתעכב אנחנו מעבירים יחד עם הבקשה שלנו גם את המשפט "אחי, כשאתה מסיים פשוט ____________" וכאן במקום הקו אנחנו יכולים להחליט מה להגיד לו לעשות עם המידע שהוא שלף. במקרה שלנו אנו מבקשים ממנו "פשוט תציג את זה על המסך", בשפה של קוד זה מופיע כ:

console.log(err,result)

התוכן שבתוך הסוגריים המסולסלות אחרי ההגדרה שמדובר פה בפונקציה function (err,result). פונקציית הקולבק בנויה לקבל את המידע עצמו או הודעת שגיאה כאשר יש תקלה. במקרה שיש תקלה בתהליך שליפת המידע אז הודעת השגיאה תיקלט לתוך הביטוי err ונוכל לטפל בה בפונקציה שלנו, ובמקרה שהכל בסדר נמצא את המידע עצמו בתוך הביטוי result. אז console.log אומר ליצור איזשהו לוג על גבי הקונסולה שזה כרגע חלון הCMD שלנו, שהתוכן שלו יהיה הודעת השגיאה ולאחריה התוצאה. אם תסתכלו על הפלט שאנו מקבלים תראו שהמילה הראשונה היא null כיון שהודעת השגיאה מגיעה ריקה כשהכל בסדר, לאחר מכן רואים את התוצאה עצמה.

בחזרה למלצר

אפשר גם להגיד למלצר משהו כמו "אחי, עזוב אותך, אני לא צריך את כל המידע, פשוט תוציא לי משם רק את השורה של המספר של הבלוק האחרון ותארוז לי אותה טו-גו, בסדר?"

למה לארוז טו-גו אתם שואלים? כי אנחנו לא באמת רוצים 'לאכול כאן' את המידע הזה, אנחנו רוצים להשתמש בו לאחר-כך. אנחנו רוצים לבצע קריאה נוספת שהפעם תביא לנו את המידע של הבלוק האחרון, ובשביל זה אנחנו רוצים לשמור את המספר של הבלוק האחרון. כדי שנוכל לבצע את הקריאה הזו.

בעצם מה שאנחנו עושים זה מה שעשינו קודם באופן ידני (אשכרה להעתיק את המספר של הבלוק האחרון מהמאפיינים הגלובלים הדינאמיים) - שיקרה עכשיו אוטומאטי. המלצר שלנו מוציא את המידע, שולף ממנו רק את השורה הרלוונטית, אורז אותה במעטפה, מעביר אותה אל הקריאה הבאה, שמזמינה מידע על בלוק, בתור הפרמטר הראשון של הקריאה.

אז הנה, בואו נעשה את זה בצורת קוד:

var head = result.head_block_number;
steem.api.getBlock(head, function(err,result){
    console.log(err,result);
});
כשאתם מעתיקים את הקוד, שימו לב שאתם לא שוכחים את הסימן ; (נקודה-פסיק) בסוף כל פקודה. זה קצת מציק אבל זה חשוב כדי שהקוד יהיה מובן למחשב. לפעמים הקוד ירוץ גם בלי הסימון הזה (בג'אווה סקריפט) אבל כדאי להקפיד כדי לא לגורם לצרות במקרים אחרים.

את הקוד הזה אנו מכניסים בין הסוגריים המסולסלות { } במקום השורה שמציגה את המידע על המסך.

יש פה 2 פקודות:

  1. הכנסה של השורה הספציפית ל'מעטפה'
  2. הפעלת קריאה לבלוק והזנת התוכן של המעטפה כפרמטר ראשון.

הרעיון של הכנסה למעטפה מתממש באמצעות הביטוי var שזה קיצור של המילה Variable כלומר משתנה, בגלל שאנחנו יכולים בכל שלב להחליף את התוכן של המעטפה במשהו אחר. על המעטפה מופיע איזשהו שם שבאמצעותו נוכל להתייחס לתוכן שבה. השם שבחרתי לה הוא head אבל תוכלו לבחור לה גם שם אחר (לא כל שם הוא קביל, זהירות). ואז כשאני צריך לקרוא לבלוק דרך המתודה הבאה, אני משתמש בביטוי head כדי להוריק את תוכן המעטפה (המספר של הבלוק האחרון) כפטרמטר הראשון.

שימו לב שהפרמטר השני של הקריאה לבלוק היא שוב פונקציית הקולבק החביבה שלנו, כאשר הבקשה היא הפעם לאכול כאן, כלומר - להציג על המסך.

בואו רק נוודא שאנחנו באותו קו, הנה הקוד המלא שצריך להופיע לנו בעורך:

var steem = require('steem');

steem.api.getDynamicGlobalProperties(function(err, result) {
    var head = result.head_block_number;
    steem.api.getBlock(head, function(err,result){
        console.log(err,result);
    });
});

אם תריצו את הקוד הזה תקבלו פלט ארוך ומסורבל של כל הבלוק שלנו. אנחנו חייבים לפרק את המידע הזה ולהכניס אותו למעטפות קטנות כדי להמשיך בעבודה שלנו על הבוט.

בשלב הזה נעצור ונמשיך בפעם הבאה בהכנת הבוט המשמעותי הראשון שלנו!!

וואו!! אני מרגיש שעברנו המון יחד, ולמדנו ים דברים חדשים. נראה לי שנעצור כאן לבנתיים. אתם מוזמנים להמשיך לחקור ולתרגל ולנסות דברים על פי אותם עקרונות. וניפגש בפעם הבאה!

הי, אשמח לתגובות ומעורבות שלכם! בעיקר מעניין אותי לדעת האם הדברים באמת מלמדים, האם זה מעניין, ואשמח גם שתשתפו הלאה אולי זה יעניין עוד אנשים. תודה!

Sort:  
תודה רבה אבי

אני ממש מחכה בשקיקה לפוסטים במדריך.
עדיין לא ניסיתי. כשאני אנסה אני אדווח על ההתנסות.

איזה יופי יורם! אלו דברים שממש משמח לשמוע

הספקתי כבר לתרגל. מחכה להמשך.

האמת שאתה צריך שהבוט הראשון יהיה בוט שמאזין למשתמש aviz85 ועושה אפוואט אוטומטי לכל פוסט שלו שמכיל את הביטוי "מדריך למתחילים..."
אני הייתי משתמש בו בשמחה :-)

תודה על הפוסט המדהים הזה , תכף קורא את הקודמים ומבצע

תודה אבי וכל הכבוד על ההשקעה!