JavaRush /وبلاگ جاوا /Random-FA /ما در حال اضافه کردن قابلیت اشتراک در گروهی از مقالات هست...
Roman Beekeeper
مرحله

ما در حال اضافه کردن قابلیت اشتراک در گروهی از مقالات هستیم. (قسمت 1) - "پروژه جاوا از A تا Z"

در گروه منتشر شد
سلام! امروز ما یک اشتراک به گروهی از مقالات در JavaRush اضافه خواهیم کرد. این مربوط به شماره JRTB-5 در GitHub است. بگذارید توضیح بدهم: JavaRush بخشی به نام مقالات دارد و در آن گروهی از مقالات وجود دارد. ایده این است که از طریق یک ربات تلگرام از یک یا چند گروه اعلان‌هایی در مورد یک مقاله جدید دریافت کنید.“پروژه جاوا از A تا Z”: افزودن قابلیت اشتراک در گروهی از مقالات.  قسمت 1 - 1

JRTB-5 را اضافه کنید

فرض کنید من به مقالاتی از گروه داستان های موفقیت علاقه مند هستم . بنابراین، من می‌خواهم در به‌روزرسانی‌های این گروه مشترک شوم و هر بار پیوندی به یک نشریه جدید دریافت کنم. به عنوان بخشی از این کار، ما باید یاد بگیریم که چگونه از API باز برای کار با گروه ها در JavaRush استفاده کنیم. درست در این لحظه چنین چیزی رسید. در اینجا پیوندی به شرح API باز وجود دارد .
دوستان! آیا می خواهید بدانید چه زمانی کد جدید برای یک پروژه یا یک مقاله جدید منتشر می شود؟ به کانال tg من بپیوندید . در آنجا مقالات، افکار و توسعه متن باز خود را با هم جمع می کنم.

فحش دادن چیست؟ حالا بیایید بفهمیم

ما هنوز در مورد فحشا صحبت نکرده ایم. برای کسانی که نمی دانند، من به طور خلاصه توضیح می دهم: اینجا مکانی است که می توانید آشکارا به API یک سرور نگاه کنید و سعی کنید برخی از درخواست ها را به آن ارائه دهید. به طور معمول، یک متجاوز درخواست های احتمالی را گروه بندی می کند. در مورد ما، سه گروه وجود دارد: forum-question ، group ، post . در هر گروه یک یا چند درخواست وجود خواهد داشت که تمام داده های لازم برای ساخت این درخواست را نشان می دهد (یعنی چه پارامترهای اضافی را می توان ارسال کرد، با آنها چه کاری انجام داد، چه روش http و غیره). من به شما توصیه می کنم بیشتر در مورد این موضوع بخوانید و تماشا کنید، زیرا این بخشی از توسعه است که تقریباً هر یک از شما با آن روبرو خواهید شد. برای فهمیدن آن، بیایید دریابیم که چند گروه در JavaRush وجود دارد. برای انجام این کار، گروه group-controller را گسترش دهید و Get request /api/1.0/rest/groups/count را انتخاب کنید . تعداد گروه‌های موجود در JavaRush را به ما برمی‌گرداند. بیایید نگاه کنیم: “پروژه جاوا از A تا Z”: افزودن قابلیت اشتراک در گروهی از مقالات.  قسمت 1 - 2تصویر نشان می دهد که این پرس و جو از چندین پارامتر (پرس و جو، نوع، فیلتر) پشتیبانی می کند. برای امتحان کردن این درخواست، باید دکمه Try it out را پیدا کنید ، پس از آن می توان این پارامترها را پیکربندی کرد: “پروژه جاوا از A تا Z”: افزودن قابلیت اشتراک در گروهی از مقالات.  قسمت 1 - 3همچنین می توانید نوع، فیلتر و پرس و جو را در آنجا پیکربندی کنید (در واقع اینجا جالب است: این یک جستجوی متنی در یک گروه). اما در حال حاضر، بیایید آن را بدون هیچ محدودیتی اجرا کنیم و ببینیم چند گروه در JavaRush وجود دارد. برای این کار بر روی Execute کلیک کنید. درست در زیر یک پاسخ (در بخش پاسخ سرور) به این درخواست وجود دارد: “پروژه جاوا از A تا Z”: افزودن قابلیت اشتراک در گروهی از مقالات.  قسمت 1 - 4می بینیم که در کل 30 گروه وجود دارد ، این درخواست در 95 میلی ثانیه تکمیل شد و مجموعه ای از سرفصل ها در پاسخ وجود دارد. در مرحله بعد، بیایید سعی کنیم برخی از پارامترها را پیکربندی کنیم. بیایید پارامتر نوع را برابر با مقدار COMPANY انتخاب کنیم و ببینیم نتیجه چگونه تغییر می کند: “پروژه جاوا از A تا Z”: افزودن قابلیت اشتراک در گروهی از مقالات.  قسمت 1 - 54 مورد وجود دارد. چگونه این را بررسی کنیم؟ آسان است: می توانید به وب سایت بروید، بخش مقاله را پیدا کنید، همه گروه ها را انتخاب کنید و فیلتر مناسب را در آنجا اضافه کنید ( https://javarush.com/groups/all?type=COMPANY ). و بله، در واقع، تنها 4 مورد از آنها وجود دارد، اگرچه در واقع سه مورد وجود دارد:D “پروژه جاوا از A تا Z”: افزودن قابلیت اشتراک در گروهی از مقالات.  قسمت 1 - 6تا اینجا مناسب است. در ضمن اگه دانشگاه ها رو چک کنیم هنوز هیچی نیست. فقط برای سرگرمی، ببینید چه اتفاقی می افتد اگر فیلتر = MY را در مرورگری تنظیم کنید که در آن وارد Javarush شده اید و وارد نشده اید. در این مقاله در Habré اطلاعات بیشتری در مورد swagger وجود دارد .

نوشتن کلاینت برای Javarush API برای گروه ها

اکنون، بر اساس API باز، یک کلاینت جاوا می‌نویسیم که می‌تواند درخواست‌ها را ارسال کند، پاسخ‌ها را دریافت کند و دقیقاً بداند که چه اشیایی وارد می‌شوند. ما همچنین اشیاء را از swagger، از بخش Models (در پایین صفحه) می گیریم. بیایید یک بسته جدید ایجاد کنیم و آن را javarushclient در کنار سرویس، مخزن بنامیم. در آینده، ما این را به یک کتابخانه جداگانه در سازمان جامعه جاروش منتقل خواهیم کرد و از آن منحصراً به عنوان یک وابستگی استفاده خواهیم کرد.
من قبلاً در مورد ایجاد کلاینت های جاوا در مقاله "راهنمای ایجاد مشتری برای Skyscanner API و انتشار آن در jCenter و Maven Central" نوشتم .
اول از همه، باید Unitrest، کتابخانه ای برای ایجاد درخواست های http به JavaRush API اضافه کنید:
<dependency>
  <groupId>com.konghq</groupId>
  <artifactId>unirest-java</artifactId>
  <version>${unirest.version}</version>
</dependency>
و نسخه را در بلوک خواص قرار دهید:
<unirest.version>3.11.01</unirest.version>
هنگامی که وابستگی داریم، می توانیم شروع به اضافه کردن کد کنیم. بیایید یک کلاینت برای گروه های JavaRushGroupClient و یک پیاده سازی در کلاس JavaRushGroupClientImpl ایجاد کنیم. اما ابتدا باید DTO (اشیاء انتقال داده) ایجاد کنید - یعنی کلاس هایی که اشیاء آنها تمام داده های لازم را برای مشتری حمل می کنند. همه مدل ها را می توان در swagger مشاهده کرد.در پایین قسمت Models وجود دارد که در آن می توانید آنها را بشمارید. این چیزی است که GroupDiscussionInfo در swagger به نظر می رسد: در بسته javarushclient، ما یک “پروژه جاوا از A تا Z”: افزودن قابلیت اشتراک در گروهی از مقالات.  قسمت 1 - 7بسته dto ایجاد می کنیم که بر اساس داده های swagger، کلاس های زیر را به آن اضافه می کنیم:
  • MeGroupInfoStatus :

    package com.github.javarushcommunity.jrtb.javarushclient.dto;
    
    /**
    * Member group status.
    */
    public enum MeGroupInfoStatus {
       UNKNOWN, CANDIDATE, INVITEE, MEMBER, EDITOR, MODERATOR, ADMINISTRATOR, BANNED
    }

  • MeGroupInfo :

    package com.github.javarushcommunity.jrtb.javarushclient.dto;
    
    import lombok.Data;
    
    /**
    * Group information related to authorized user. If there is no user - will be null.
    */
    @Data
    public class MeGroupInfo {
       private MeGroupInfoStatus status;
       private Integer userGroupId;
    }

  • GroupInfoType :

    package com.github.javarushcommunity.jrtb.javarushclient.dto;
    
    /**
    * Group Info type;
    */
    public enum GroupInfoType {
       UNKNOWN, CITY, COMPANY, COLLEGE, TECH, SPECIAL, COUNTRY
    }

  • User DiscussionInfo :

    package com.github.javarushcommunity.jrtb.javarushclient.dto;
    
    import lombok.Data;
    
    /**
    * DTO for User discussion info.
    */
    @Data
    public class UserDiscussionInfo {
    
       private Boolean isBookmarked;
       private Integer lastTime;
       private Integer newCommentsCount;
    }

  • GroupVisibilitysdtatus :

    package com.github.javarushcommunity.jrtb.javarushclient.dto;
    
    /**
    * Group Visibility status.
    */
    public enum GroupVisibilityStatus {
       UNKNOWN, RESTRICTED, PUBLIC, PROTECTED, PRIVATE, DISABLED, DELETED
    }

  • سپس - GroupInfo :

    package com.github.javarushcommunity.jrtb.javarushclient.dto;
    
    import lombok.Data;
    import lombok.ToString;
    
    /**
    * Group Info DTO class.
    */
    @Data
    @ToString
    public class GroupInfo {
    
       private Integer id;
       private String avatarUrl;
       private String createTime;
       private String description;
       private String key;
       private Integer levelToEditor;
       private MeGroupInfo meGroupInfo;
       private String pictureUrl;
       private String title;
       private GroupInfoType type;
       private Integer userCount;
       private GroupVisibilityStatus visibilityStatus;
    }

از آنجایی که GroupInfo و GroupDiscussionInfo تقریباً یکسان هستند، بیایید آنها را در ارث به هم پیوند دهیم - GroupDiscussionInfo :
package com.github.javarushcommunity.jrtb.javarushclient.dto;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;

/**
* Group discussion info class.
*/
@EqualsAndHashCode(callSuper = true)
@Data
@ToString(callSuper = true)
public class GroupDiscussionInfo extends GroupInfo {

   private UserDiscussionInfo userDiscussionInfo;
   private Integer commentsCount;
}
همچنین برای درخواست GroupFilter به یک فیلتر نیاز داریم :
package com.github.javarushcommunity.jrtb.javarushclient.dto;

/**
* Filters for group requests.
*/
public enum GroupFilter {

   UNKNOWN, MY, ALL
}
در درخواست دریافت با ID، GroupDiscussionInfo را برمی گرداند و در درخواست مجموعه ای از گروه ها، می توانید هم GroupInfo و هم GroupDiscussionInfo را دریافت کنید. از آنجایی که درخواست ها می توانند نوع، پرس و جو، فیلتر، افست و محدودیت داشته باشند، بیایید یک کلاس GroupRequestArgs جداگانه ایجاد کنیم و آن را به یک کلاس سازنده تبدیل کنیم (بخوانید الگوی سازنده چیست):
package com.github.javarushcommunity.jrtb.javarushclient.dto;

import lombok.*;

import java.util.HashMap;
import java.util.Map;

import static java.util.Objects.nonNull;

/**
* Request arguments for group requests.
*/
@Builder
@Getter
public class GroupRequestArgs {

   private final String query;
   private final GroupInfoType type;
   private final GroupFilter filter;

   /**
    * specified where to start getting groups
    */
   private final Integer offset;
   /**
    * Limited number of groups.
    */
   private final Integer limit;

   public Map populateQueries() {
       Map queries = new HashMap<>();
       if(nonNull(query)) {
           queries.put("query", query);
       }
       if(nonNull(type)) {
           queries.put("type", type);
       }
       if(nonNull(filter)) {
           queries.put("filter", filter);
       }
       if(nonNull(offset)) {
           queries.put("offset", offset);
       }
       if(nonNull(limit)) {
           queries.put("limit", limit);
       }
       return queries;
   }
}
برای جستجوی تعداد گروه ها کمی متفاوت است. فقط پرس و جو، نوع و فیلتر دارد. و به نظر می رسد که شما نمی خواهید کد را تکرار کنید. در عین حال، اگر شروع به ترکیب آنها کنید، هنگام کار با سازندگان زشت به نظر می رسد. بنابراین تصمیم گرفتم آنها را جدا کنم و کد را تکرار کنم. این چیزی است که GroupCountRequestArgs به نظر می رسد :
package com.github.javarushcommunity.jrtb.javarushclient.dto;

import lombok.Builder;
import lombok.Getter;

import java.util.HashMap;
import java.util.Map;

import static java.util.Objects.nonNull;

/**
* Request arguments for group count requests.
*/
@Builder
@Getter
public class GroupsCountRequestArgs {
   private final String query;
   private final GroupInfoType type;
   private final GroupFilter filter;

   public Map populateQueries() {
       Map queries = new HashMap<>();
       if (nonNull(query)) {
           queries.put("query", query);
       }
       if (nonNull(type)) {
           queries.put("type", type);
       }
       if (nonNull(filter)) {
           queries.put("filter", filter);
       }
       return queries;
   }
}
بله، من اشاره نکردم که دو کلاس آخر یک متد populateQueries دارند که نقشه را برای ایجاد یک کوئری آماده می کند (بعداً آن را خواهید دید). بر اساس کلاس هایی که در بالا توضیح داده شد، اجازه دهید یک رابط برای JavaRushGroupClient ایجاد کنیم :
package com.github.javarushcommunity.jrtb.javarushclient;

import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupDiscussionInfo;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupInfo;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupRequestArgs;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupsCountRequestArgs;

import java.util.List;

/**
 * Client for Javarush Open API corresponds to Groups.
 */
public interface JavaRushGroupClient {

    /**
     * Get all the {@link GroupInfo} filtered by provided {@link GroupRequestArgs}.
     *
     * @param requestArgs provided {@link GroupRequestArgs}.
     * @return the collection of the {@link GroupInfo} objects.
     */
    List<GroupInfo> getGroupList(GroupRequestArgs requestArgs);

    /**
     * Get all the {@link GroupDiscussionInfo} filtered by provided {@link GroupRequestArgs}.
     *
     * @param requestArgs provided {@link GroupRequestArgs}
     * @return the collection of the {@link GroupDiscussionInfo} objects.
     */
    List<GroupDiscussionInfo> getGroupDiscussionList(GroupRequestArgs requestArgs);

    /**
     * Get count of groups filtered by provided {@link GroupRequestArgs}.
     *
     * @param countRequestArgs provided {@link GroupsCountRequestArgs}.
     * @return the count of the groups.
     */
    Integer getGroupCount(GroupsCountRequestArgs countRequestArgs);

    /**
     * Get {@link GroupDiscussionInfo} by provided ID.
     *
     * @param id provided ID.
     * @return {@link GroupDiscussionInfo} object.
     */
    GroupDiscussionInfo getGroupById(Integer id);
}
دو درخواست مختلف برای موردی که می خواهیم اطلاعات GroupInfo یا GroupDiscussionInfo را دریافت کنیم اضافه می شود. در غیر این صورت، این درخواست ها یکسان هستند و تنها تفاوت این است که در یکی پرچم includeDiscussion درست و در دیگری نادرست خواهد بود. بنابراین، 4 روش وجود داشت، نه سه روش. حالا بیایید پیاده سازی را شروع کنیم:
package com.github.javarushcommunity.jrtb.javarushclient;

import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupDiscussionInfo;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupInfo;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupRequestArgs;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupsCountRequestArgs;
import kong.unirest.GenericType;
import kong.unirest.Unirest;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.List;

/**
* Implementation of the {@link JavaRushGroupClient} interface.
*/
@Component
public class JavaRushGroupClientImpl implements JavaRushGroupClient {

   private final String javarushApiGroupPath;

   public JavaRushGroupClientImpl(@Value("${javarush.api.path}") String javarushApi) {
       this.javarushApiGroupPath = javarushApi + "/groups";
   }

   @Override
   public List<GroupInfo> getGroupList(GroupRequestArgs requestArgs) {
       return Unirest.get(javarushApiGroupPath)
               .queryString(requestArgs.populateQueries())
               .asObject(new GenericType<list<GroupInfo>>() {
               })
               .getBody();
   }

   @Override
   public List<GroupDiscussionInfo> getGroupDiscussionList(GroupRequestArgs requestArgs) {
       return Unirest.get(javarushApiGroupPath)
               .queryString(requestArgs.populateQueries())
               .asObject(new GenericType<list<GroupDiscussionInfo>>() {
               })
               .getBody();
   }

   @Override
   public Integer getGroupCount(GroupsCountRequestArgs countRequestArgs) {
       return Integer.valueOf(
               Unirest.get(String.format("%s/count", javarushApiGroupPath))
                       .queryString(countRequestArgs.populateQueries())
                       .asString()
                       .getBody()
       );
   }

   @Override
   public GroupDiscussionInfo getGroupById(Integer id) {
       return Unirest.get(String.format("%s/group%s", javarushApiGroupPath, id.toString()))
               .asObject(GroupDiscussionInfo.class)
               .getBody();
   }


}
من مسیر را به API در سازنده با استفاده از حاشیه نویسی Value از قبل آشنا اضافه می کنم. به این معنی است که مقدار داخل حاشیه نویسی با فیلدی در فایل خواص مطابقت دارد. بنابراین، بیایید یک خط جدید به application.properties اضافه کنیم:
javarush.api.path=https://javarush.com/api/1.0/rest
این مقدار اکنون برای همه کلاینت های API در یک مکان خواهد بود و اگر مسیر API تغییر کند، به سرعت آن را به روز می کنیم. قبلاً، میخ ها را با میکروسکوپ چکش می کردم، از طریق Unirest پاسخی از یک درخواست http دریافت می کردم، آن را به یک رشته ترجمه می کردم و سپس این رشته را از طریق جکسون تجزیه می کردم... ترسناک، خسته کننده بود و به چیزهای اضافی زیادی نیاز داشت. در این کتابخانه می توانید ببینید که چگونه به نظر می رسد. به محض اینکه به دستم رسید، همه چیز را بازسازی می کنم.
هر کسی که مایل است سعی کند این کتابخانه را به روز کند - اشیاء دریافت کننده را فقط با استفاده از ابزارهای کتابخانه unirest اضافه کند - در یک پیام شخصی یا به عنوان یک شماره جدید در خود کتابخانه بنویسد. این یک تجربه کاری واقعی برای شما خواهد بود، اما من اهمیتی نمی‌دهم. من یک بررسی کامل کد انجام خواهم داد و در صورت لزوم کمک خواهم کرد.
حال سوال این است: آیا کد ما همانطور که انتظار داریم کار می کند؟ پاسخ آسان است: فقط باید برای آنها تست بنویسید. همانطور که بیش از یک بار گفته ام، توسعه دهندگان باید بتوانند تست بنویسند. بنابراین، با استفاده از Swagger UI، درخواست‌هایی را ارسال می‌کنیم، به پاسخ‌ها نگاه می‌کنیم و آنها را به عنوان نتیجه مورد انتظار در تست‌ها جایگزین می‌کنیم. ممکن است بلافاصله متوجه شده باشید که تعداد گروه ها ثابت نیست و می تواند تغییر کند. و حق با شماست. تنها سوال این است که هر چند وقت یکبار این عدد تغییر می کند؟ خیلی به ندرت، بنابراین در طول چند ماه می توان گفت که این مقدار ثابت خواهد بود. و اگر چیزی تغییر کرد، ما تست ها را به روز خواهیم کرد. Meet - JavaRushGroupClientTest:
package com.github.javarushcommunity.jrtb.javarushclient;

import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupDiscussionInfo;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupInfo;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupRequestArgs;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupsCountRequestArgs;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.List;

import static com.github.javarushcommunity.jrtb.javarushclient.dto.GroupInfoType.TECH;

@DisplayName("Integration-level testing for JavaRushGroupClientImplTest")
class JavaRushGroupClientTest {

   private final JavaRushGroupClient groupClient = new JavaRushGroupClientImpl("https://javarush.com/api/1.0/rest");

   @Test
   public void shouldProperlyGetGroupsWithEmptyArgs() {
       //given
       GroupRequestArgs args = GroupRequestArgs.builder().build();

       //when
       List<GroupInfo> groupList = groupClient.getGroupList(args);

       //then
       Assertions.assertNotNull(groupList);
       Assertions.assertFalse(groupList.isEmpty());
   }

   @Test
   public void shouldProperlyGetWithOffSetAndLimit() {
       //given
       GroupRequestArgs args = GroupRequestArgs.builder()
               .offset(1)
               .limit(3)
               .build();

       //when
       List<GroupInfo> groupList = groupClient.getGroupList(args);

       //then
       Assertions.assertNotNull(groupList);
       Assertions.assertEquals(3, groupList.size());
   }

   @Test
   public void shouldProperlyGetGroupsDiscWithEmptyArgs() {
       //given
       GroupRequestArgs args = GroupRequestArgs.builder().build();

       //when
       List<GroupDiscussionInfo> groupList = groupClient.getGroupDiscussionList(args);

       //then
       Assertions.assertNotNull(groupList);
       Assertions.assertFalse(groupList.isEmpty());
   }

   @Test
   public void shouldProperlyGetGroupDiscWithOffSetAndLimit() {
       //given
       GroupRequestArgs args = GroupRequestArgs.builder()
               .offset(1)
               .limit(3)
               .build();

       //when
       List<GroupDiscussionInfo> groupList = groupClient.getGroupDiscussionList(args);

       //then
       Assertions.assertNotNull(groupList);
       Assertions.assertEquals(3, groupList.size());
   }

   @Test
   public void shouldProperlyGetGroupCount() {
       //given
       GroupsCountRequestArgs args = GroupsCountRequestArgs.builder().build();

       //when
       Integer groupCount = groupClient.getGroupCount(args);

       //then
       Assertions.assertEquals(30, groupCount);
   }

   @Test
   public void shouldProperlyGetGroupTECHCount() {
       //given
       GroupsCountRequestArgs args = GroupsCountRequestArgs.builder()
               .type(TECH)
               .build();

       //when
       Integer groupCount = groupClient.getGroupCount(args);

       //then
       Assertions.assertEquals(7, groupCount);
   }

   @Test
   public void shouldProperlyGetGroupById() {
       //given
       Integer androidGroupId = 16;

       //when
       GroupDiscussionInfo groupById = groupClient.getGroupById(androidGroupId);

       //then
       Assertions.assertNotNull(groupById);
       Assertions.assertEquals(16, groupById.getId());
       Assertions.assertEquals(TECH, groupById.getType());
       Assertions.assertEquals("android", groupById.getKey());
   }
}
تست ها به همان سبک قبلی نوشته شده اند. برای هر درخواست چندین تست وجود دارد. آزمایش همه چیز فایده ای ندارد، زیرا من فکر می کنم که این API قبلاً به بهترین شکل آزمایش شده است.

نتیجه

به عنوان بخشی از این مقاله، ما یک سرویس گیرنده جاوا برای گروه ها به JavaRush API اضافه کردیم. همانطور که می گویند، زندگی کنید و یاد بگیرید. در حین نوشتن این مشتری، از مستندات آنها استفاده کردم و به راحتی از کار با اشیایی که آنها ارائه می دهند استفاده کردم. توجه شما را به وظیفه ای که پیشنهاد کردم جلب می کنم. اگر کسی علاقه مند است برای من پیام خصوصی بنویسد، مطمئنم که تجربه بسیار جالبی خواهد بود. این قسمت اول بود در مرحله دوم، دستور افزودن را مستقیماً پیاده سازی می کنیم و (اگر آن را در یک مقاله قرار دهیم) دریافت لیستی از گروه هایی که کاربر در آنها مشترک است را اضافه می کنیم. در مرحله بعد، هرکسی که تمایل و استعداد نوشتن متن برای ربات را دارد، لطفا برای من در پی ام بنویسد. من در این مورد متخصص نیستم و هر کمکی می تواند بسیار مفید باشد. بیایید همه اینها را به عنوان توسعه منبع باز رسمی کنیم، جالب خواهد بود! خوب، طبق معمول - لایک کنید، مشترک شوید، زنگ بزنید ، به پروژه ما ستاره بدهید ، نظرات خود را بنویسید و به مقاله امتیاز دهید!
لینک های مفید

فهرستی از تمام مواد این مجموعه در ابتدای این مقاله است.

نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION