<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Darío Fernández Torre on Medium]]></title>
        <description><![CDATA[Stories by Darío Fernández Torre on Medium]]></description>
        <link>https://medium.com/@darioft?source=rss-737cb9a07c91------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*7SCCi1EkVa34JTReRchZpw.jpeg</url>
            <title>Stories by Darío Fernández Torre on Medium</title>
            <link>https://medium.com/@darioft?source=rss-737cb9a07c91------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Wed, 08 Apr 2026 06:34:05 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@darioft/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Automating LinkedIn Replies with OpenAI: Guided Tour of Building with Angular 17 and Azure…]]></title>
            <link>https://medium.com/@darioft/automating-linkedin-replies-with-openai-guided-tour-of-building-with-angular-17-and-azure-38909d8a1b55?source=rss-737cb9a07c91------2</link>
            <guid isPermaLink="false">https://medium.com/p/38909d8a1b55</guid>
            <category><![CDATA[automation]]></category>
            <category><![CDATA[azure-functions]]></category>
            <category><![CDATA[openai]]></category>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[chatgpt]]></category>
            <dc:creator><![CDATA[Darío Fernández Torre]]></dc:creator>
            <pubDate>Wed, 10 Jan 2024 17:04:20 GMT</pubDate>
            <atom:updated>2024-01-10T17:04:20.503Z</atom:updated>
            <content:encoded><![CDATA[<h3>Automating LinkedIn Replies with OpenAI: Guided Tour of Building with Angular 17 and Azure Functions</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*A-9iioYMo0yKmK_pjF1Gkw.png" /><figcaption>Automating Professional Networking</figcaption></figure><p>Explore the integration of an Angular application with Python Azure Functions to manage LinkedIn communications effectively. This guide provides a practical overview of building a robust automated response application, enhancing your professional networking with technology.</p><h4>Embracing Angular 17 and Material for an Intuitive UI</h4><p>Imagine entering a meticulously arranged office, where each tool and document is perfectly within reach, enhancing your workflow with seamless efficiency. This mirrors the user experience that Angular 17 and Material have enabled me to create for this LinkedIn response application. Angular 17’s modular components act as the flexible and customizable building blocks of the app, akin to the adaptable, multi-functional furniture in a dynamic office space. Material Design intertwines aesthetics with usability, ensuring that the application is not just highly functional but also visually appealing and engaging for the user.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fmTe9X_6UBlf26EBFoJC6g.png" /><figcaption>Screenshot of the Automated LinkedIn Response Application</figcaption></figure><p>The user interface is divided into two primary sections, striking a balance between efficiency and control. On one side, an organized list of messages serves as your digital incoming mail tray. Each non-sponsored message from your LinkedIn inbox is clearly displayed, waiting for your attention, but not crowding your space. You can easily navigate through them, selecting one to focus on without losing sight of the others. Here’s this standalone component’s code:</p><pre>&lt;mat-spinner *ngIf=&quot;isLoading&quot;&gt;&lt;/mat-spinner&gt;<br><br>&lt;div *ngIf=&quot;!isLoading&quot;&gt;<br>  &lt;div<br>    *ngFor=&quot;let message of messages&quot;<br>    class=&quot;message-card&quot;<br>    (click)=&quot;selectMessage(message)&quot;<br>  &gt;<br>    &lt;mat-card&gt;<br>      &lt;mat-card-header&gt;<br>        &lt;mat-card-title&gt;{{ message.senderName }}&lt;/mat-card-title&gt;<br>      &lt;/mat-card-header&gt;<br>      &lt;mat-card-content&gt;<br>        {{ message.message }}<br>      &lt;/mat-card-content&gt;<br>      &lt;mat-card-footer *ngIf=&quot;selectedMessage === message&quot;&gt;<br>        &lt;mat-progress-bar<br>          mode=&quot;indeterminate&quot;<br>          style=&quot;margin-top: 1rem&quot;<br>        &gt;&lt;/mat-progress-bar&gt;<br>      &lt;/mat-card-footer&gt;<br>    &lt;/mat-card&gt;<br>  &lt;/div&gt;<br>&lt;/div&gt;</pre><pre>import { Component, OnInit, Output, EventEmitter } from &#39;@angular/core&#39;;<br>import { LinkedInService } from &#39;../linked-in.service&#39;;<br>import { LinkedInMessage } from &#39;../linked-in-message&#39;;<br>import { MatListModule } from &#39;@angular/material/list&#39;;<br>import { MatCardModule } from &#39;@angular/material/card&#39;;<br>import { MatProgressSpinnerModule } from &#39;@angular/material/progress-spinner&#39;;<br>import { MatProgressBarModule } from &#39;@angular/material/progress-bar&#39;;<br><br>import { CommonModule } from &#39;@angular/common&#39;;<br>import { MessageUpdateService } from &#39;../message-update.service&#39;;<br><br>@Component({<br>  selector: &#39;app-messages&#39;,<br>  standalone: true,<br>  imports: [<br>    CommonModule,<br>    MatListModule,<br>    MatCardModule,<br>    MatProgressSpinnerModule,<br>    MatProgressBarModule,<br>  ],<br>  templateUrl: &#39;./messages.component.html&#39;,<br>  styleUrls: [&#39;./messages.component.css&#39;],<br>})<br>export class MessagesComponent implements OnInit {<br>  messages: LinkedInMessage[] = [];<br>  selectedMessage!: LinkedInMessage;<br>  isLoading = true;<br><br>  @Output() messageSelected = new EventEmitter&lt;LinkedInMessage&gt;();<br><br>  constructor(<br>    private linkedInService: LinkedInService,<br>    private messageUpdateService: MessageUpdateService<br>  ) {}<br><br>  ngOnInit(): void {<br>    this.linkedInService.getMessages().subscribe((data: any) =&gt; {<br>      console.log(data);<br>      this.isLoading = false;<br>      this.messages = this.linkedInService.processMessages(data);<br>    });<br><br>    this.messageUpdateService.currentMessageSent.subscribe((urn) =&gt; {<br>      if (urn) {<br>        this.onMessageSent(urn);<br>      }<br>    });<br>  }<br><br>  selectMessage(message: LinkedInMessage): void {<br>    this.selectedMessage = message;<br>    this.messageSelected.emit(this.selectedMessage);<br>  }<br><br>  onMessageSent(urn: string): void {<br>    this.messages = this.messages.filter(<br>      (message) =&gt; message.conversationUrn !== urn<br>    );<br>  }<br>}</pre><p>On the other side of the screen, magic happens. After selecting a message a variety of response cues are offered. These are far from standard automated replies; they are identifiers for tailored prompts that get combined with the context of each message, like having a personal assistant who understands the nuances of tone and content. After generating a response, you can swiftly review it, add personal touches, and send it off, all within the same application. It’s about making the reply process quicker, not eliminating the personalized approach. Let’s take a look at the code:</p><pre>&lt;div class=&quot;response-editor-container&quot;&gt;<br>  &lt;mat-form-field appearance=&quot;fill&quot;&gt;<br>    &lt;mat-label&gt;Response Type&lt;/mat-label&gt;<br>    &lt;mat-select<br>      [(ngModel)]=&quot;selectedResponseOption&quot;<br>      [disabled]=&quot;isGeneratingResponse || isSending&quot;<br>    &gt;<br>      &lt;mat-option value=&quot;accept&quot;&gt;Accept&lt;/mat-option&gt;<br>      &lt;mat-option value=&quot;reject&quot;&gt;Reject&lt;/mat-option&gt;<br>      &lt;mat-option value=&quot;askMore&quot;&gt;Ask for More Details&lt;/mat-option&gt;<br>      &lt;mat-option value=&quot;custom&quot;&gt;Custom&lt;/mat-option&gt;<br>    &lt;/mat-select&gt;<br>  &lt;/mat-form-field&gt;<br><br>  &lt;mat-form-field appearance=&quot;fill&quot; *ngIf=&quot;selectedResponseOption === &#39;custom&#39;&quot;&gt;<br>    &lt;mat-label&gt;Custom Response&lt;/mat-label&gt;<br>    &lt;input matInput [(ngModel)]=&quot;customResponse&quot; /&gt;<br>  &lt;/mat-form-field&gt;<br><br>  &lt;mat-form-field appearance=&quot;fill&quot; class=&quot;response-editor&quot;&gt;<br>    &lt;mat-label&gt;Response&lt;/mat-label&gt;<br>    &lt;textarea<br>      matInput<br>      [(ngModel)]=&quot;response&quot;<br>      [disabled]=&quot;isGeneratingResponse || isSending&quot;<br>      #responseTextarea<br>    &gt;&lt;/textarea&gt;<br>  &lt;/mat-form-field&gt;<br><br>  &lt;div class=&quot;button-container&quot;&gt;<br>    &lt;button<br>      mat-raised-button<br>      color=&quot;accent&quot;<br>      (click)=&quot;generateResponse()&quot;<br>      [disabled]=&quot;isGeneratingResponse || isSending&quot;<br>    &gt;<br>      &lt;mat-spinner *ngIf=&quot;isGeneratingResponse&quot; diameter=&quot;20&quot;&gt;&lt;/mat-spinner&gt;<br>      &lt;span *ngIf=&quot;!isGeneratingResponse&quot;&gt;Generate Response&lt;/span&gt;<br>    &lt;/button&gt;<br><br>    &lt;button<br>      mat-raised-button<br>      color=&quot;primary&quot;<br>      (click)=&quot;sendMessage()&quot;<br>      [disabled]=&quot;isGeneratingResponse || isSending&quot;<br>      style=&quot;float: right&quot;<br>    &gt;<br>      &lt;mat-spinner *ngIf=&quot;isSending&quot; diameter=&quot;20&quot;&gt;&lt;/mat-spinner&gt;<br>      &lt;span *ngIf=&quot;!isSending&quot;&gt;Send&lt;/span&gt;<br>    &lt;/button&gt;<br>  &lt;/div&gt;<br>&lt;/div&gt;</pre><pre>import {<br>  Component,<br>  ElementRef,<br>  Input,<br>  SimpleChanges,<br>  ViewChild,<br>} from &#39;@angular/core&#39;;<br>import { CommonModule } from &#39;@angular/common&#39;;<br>import { LinkedInService } from &#39;../linked-in.service&#39;;<br>import { FormsModule } from &#39;@angular/forms&#39;;<br>import { MatFormFieldModule } from &#39;@angular/material/form-field&#39;;<br>import { MatInputModule } from &#39;@angular/material/input&#39;;<br>import { MatButtonModule } from &#39;@angular/material/button&#39;;<br>import { MatProgressSpinnerModule } from &#39;@angular/material/progress-spinner&#39;;<br>import { MatSnackBar, MatSnackBarModule } from &#39;@angular/material/snack-bar&#39;;<br>import { MatSelectModule } from &#39;@angular/material/select&#39;;<br>import { OpenAIService } from &#39;../open-ai.service&#39;;<br>import { MessageUpdateService } from &#39;../message-update.service&#39;;<br><br>@Component({<br>  selector: &#39;app-response-editor&#39;,<br>  standalone: true,<br>  imports: [<br>    CommonModule,<br>    FormsModule,<br>    MatFormFieldModule,<br>    MatInputModule,<br>    MatButtonModule,<br>    MatProgressSpinnerModule,<br>    MatSnackBarModule,<br>    MatSelectModule,<br>  ],<br>  templateUrl: &#39;./response-editor.component.html&#39;,<br>  styleUrl: &#39;./response-editor.component.css&#39;,<br>})<br>export class ResponseEditorComponent {<br>  @Input() selectedMessage: any;<br>  @ViewChild(&#39;responseTextarea&#39;) responseTextarea!: ElementRef;<br>  response: string = &#39;&#39;;<br>  isSending: boolean = false;<br>  isGeneratingResponse: boolean = false;<br>  public customResponse: string = &#39;&#39;;<br><br>  selectedResponseOption: string = &#39;askMore&#39;;<br><br>  constructor(<br>    private linkedInService: LinkedInService,<br>    private messageUpdateService: MessageUpdateService,<br>    private openAIService: OpenAIService,<br>    private snackBar: MatSnackBar<br>  ) {}<br><br>  ngOnChanges(changes: SimpleChanges): void {<br>    if (<br>      changes[&#39;selectedMessage&#39;] &amp;&amp;<br>      !changes[&#39;selectedMessage&#39;].isFirstChange()<br>    ) {<br>      this.response = &#39;&#39;;<br>    }<br>  }<br><br>  private getPersonalizedPrompt(): string {<br>    switch (this.selectedResponseOption) {<br>      case &#39;accept&#39;:<br>        return `Generate an accepting response for ${this.selectedMessage.senderName}, demostrating interest in the following offer:\n\n${this.selectedMessage.message}\n\n`;<br>      case &#39;reject&#39;:<br>        return `Generate a rejection response for ${this.selectedMessage.senderName}, being not interested in the following offer:\n\n${this.selectedMessage.message}\n\n`;<br>      case &#39;askMore&#39;:<br>        return `Generate a response for ${this.selectedMessage.senderName}, asking to know more about the following offer:\n\n${this.selectedMessage.message}\n\n`;<br>      case &#39;custom&#39;:<br>        return this.customResponse;<br>      default:<br>        return &#39;&#39;;<br>    }<br>  }<br><br>  adjustTextareaHeight(): void {<br>    const textarea: HTMLTextAreaElement = this.responseTextarea.nativeElement;<br>    textarea.style.height = &#39;auto&#39;;<br>    textarea.style.height = `${textarea.scrollHeight}px`;<br>  }<br><br>  sendMessage(): void {<br>    if (this.response.length &gt; 0 &amp;&amp; this.selectedMessage.conversationUrn) {<br>      this.isSending = true;<br>      const urnParts = this.selectedMessage.conversationUrn.split(&#39;:&#39;);<br>      const conversationUrnId = urnParts[urnParts.length - 1];<br><br>      this.linkedInService<br>        .sendMessage(this.response, conversationUrnId)<br>        .subscribe({<br>          next: (response) =&gt; {<br>            console.log(&#39;Response on success:&#39;, response);<br>            this.isSending = false;<br>            this.showMessage(&#39;Message sent successfully&#39;, &#39;Success&#39;);<br>            this.messageUpdateService.messageSent(<br>              this.selectedMessage.conversationUrn<br>            );<br>            this.selectedMessage = null;<br>          },<br>          error: (err) =&gt; {<br>            console.log(&#39;Error response:&#39;, err);<br>            this.isSending = false;<br>            const errorMessage = err.error<br>              ? err.error<br>              : &#39;Failed to send message&#39;;<br>            this.showMessage(errorMessage, &#39;Error&#39;);<br>          },<br>        });<br>    } else {<br>      console.log(&#39;No response or URN.&#39;);<br>    }<br>  }<br><br>  showMessage(message: string, action: string) {<br>    this.snackBar.open(message, action, {<br>      duration: 3000,<br>    });<br>  }<br><br>  generateResponse(): void {<br>    this.isGeneratingResponse = true;<br><br>    const systemPrompt =<br>      &quot;You are a helpful assistant. You&#39;re in charge of replying the LinkedIn messages received for Darío, always answer as him, never mention being an assistant. Be polite and brief. Darío is a senior graduate software engineer. Always answer in the offer&#39;s language (if spanish, use Argentinian spanish)&quot;;<br>    const personalizedPrompt = this.getPersonalizedPrompt();<br>    const message = `${personalizedPrompt}\n\n${this.selectedMessage.message}\n\n`;<br><br>    this.openAIService<br>      .generateResponse(systemPrompt, message)<br>      .subscribe((response) =&gt; {<br>        setTimeout(() =&gt; this.adjustTextareaHeight(), 0);<br>        this.isGeneratingResponse = false;<br>        this.response = response.content;<br>      });<br>  }<br>}</pre><p>Angular Material lends these components a refined and professional aesthetic. Buttons, dropdowns, and animations are smooth and intuitive, transforming the experience from simply efficient to actively enjoyable. It’s crucial to understand that this setup is a basic, initial approach, tailored for rapid testing of the system’s capabilities in a minimal viable setup. This configuration is not intended for production but is specifically designed for this example, to quickly evaluate and demonstrate the potential and user-friendliness of the application without delving into more advanced development.</p><p>Finally, within the application’s architecture, the Angular services play a vital yet understated role. They are efficiently designed to interact with our Azure Functions endpoints, handling the necessary data processing and communications. This aspect of the system ensures that while the components handle the user interaction gracefully, the services robustly manage the communication with the backend and deal with minor logic stuff, maintaining the application’s overall performance and reliability. Code snippets in this case won’t improve the understanding of the app as the services implementation consist of pretty vanilla REST API calls and model handling.</p><h4>Innovating with Azure Functions and Python</h4><p>Initially, I sought to leverage LinkedIn’s official API for creating a seamless automated response system. However, I quickly encountered its restrictive nature, especially for individual developers. It became evident that LinkedIn’s policies are stringently designed to deter automation in many aspects, particularly concerning user interactions and message access. This revelation was akin to preparing a gourmet dinner only to realize the limitations of the kitchen tools at hand. Despite automation’s pervasive nature in many sectors, including recruiting, LinkedIn’s stance on preserving user privacy and preventing unsolicited communications remains steadfast and restrictive.</p><p>While the official API provides <em>certain </em>functionalities, access to crucial features like reading and responding to messages or accessing detailed user data is heavily guarded or entirely off-limits for most individual developers. This is likely a measure to protect users from spam and preserve the platform’s integrity but poses significant challenges for those looking to innovate or streamline their networking processes. Consequently, any endeavor to automate interactions or access more in-depth data requires creative thinking and a cautious approach to adhere to LinkedIn’s terms of service and ethical guidelines.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*c5KOx8Kni3cGYsZQ7933ig.png" /><figcaption>Navigating the Quirks of LinkedIn’s API</figcaption></figure><p>Given these constraints, I turned my attention to exploring other avenues as a purely experimental endeavor. While an <a href="https://github.com/tomquirk/linkedin-api">unofficial API</a> offered a glimpse into what might be possible, it’s important to note that such alternatives are not sanctioned by LinkedIn and often conflict with their Terms of Service. Therefore, I conducted my explorations on non-professional test accounts, <em>ensuring that this foray into automation was harmless and purely for </em><strong><em>educational purposes</em></strong>. The journey, albeit filled with challenges, shed light on the intricacies of navigating API restrictions and the broader implications of automation in professional networking.</p><p>Being versed in both C# and Python made the transition smooth for this project. Although I typically work with C# due to its .NET compatibility, the need for a Python-based unofficial API library wasn’t a hurdle. Thanks to Azure Functions’ support for multiple languages, I could easily leverage my Python skills to implement the necessary features, effectively instructing the app on managing and automating LinkedIn responses. This versatility in skills and tools ensured the project’s success with minimal friction.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kyFW-TpLTTUtlBCak3gd_g.png" /><figcaption>Unleashing some potential of Azure Functions</figcaption></figure><p>Within Azure Functions, I orchestrated a trio of scripts specifically tailored to automate the LinkedIn communication process. One function is responsible for communicating with the OpenAI service, crafting intelligent and context-aware responses. The other two functions are dedicated to LinkedIn interactions: one for retrieving incoming messages and another for dispatching crafted replies. Together, these functions form a streamlined process, each playing a crucial role in the automated system.</p><p>I’m presenting the code for these functions here for educational purposes of course, allowing you to understand and perhaps replicate or extend this functionality in your own projects. The first function is a direct line to OpenAI’s powerful models, while the other two manage the intricacies of LinkedIn’s messaging. This setup demonstrates a practical application of Azure Functions and Python scripting, showing how they can be combined to tackle real-world challenges in professional networking. By sharing this, I hope to inspire and inform your own explorations into the world of automation, all while emphasizing the importance of ethical and cautious experimentation within the boundaries of service terms and personal integrity.</p><pre>import json<br>import os<br>import logging<br>import requests<br>import azure.functions as func<br>from linkedin_api import Linkedin<br><br>app = func.FunctionApp(http_auth_level=func.AuthLevel.ANONYMOUS)<br><br>@app.route(route=&quot;generateresponse&quot;)<br>def generateresponse(req: func.HttpRequest) -&gt; func.HttpResponse:<br>    logging.info(&#39;Python HTTP trigger function processed a request.&#39;)<br><br>    try:<br>        # Parse the request body<br>        req_body = req.get_json()<br><br>        # OpenAI Chat API endpoint and key<br>        openai_endpoint = os.environ.get(&#39;OPENAI_ENDPOINT&#39;)<br>        openai_api_key = os.environ.get(&#39;OPENAI_API_KEY&#39;)<br><br>        headers = {<br>            &#39;Content-Type&#39;: &#39;application/json&#39;,<br>            &#39;api-key&#39;: f&#39;{openai_api_key}&#39;<br>        }<br><br>        # Sending post request to OpenAI Chat API<br>        response = requests.post(openai_endpoint, headers=headers, json=req_body)<br>        response_json = response.json()<br><br>        # Extracting the message content<br>        if &#39;choices&#39; in response_json and len(response_json[&#39;choices&#39;]) &gt; 0:<br>            message_content = response_json[&#39;choices&#39;][0][&#39;message&#39;][&#39;content&#39;]<br>        else:<br>            message_content = &quot;No response generated.&quot;<br><br>        # Returning only the message content<br>        return func.HttpResponse(<br>            body=json.dumps({&quot;content&quot;: message_content}),<br>            status_code=200,<br>            mimetype=&#39;application/json&#39;<br>        )<br>    except Exception as e:<br>        logging.error(f&#39;Error: {str(e)}&#39;)<br>        return func.HttpResponse(<br>            body=json.dumps({&#39;error&#39;: str(e)}),<br>            status_code=500,<br>            mimetype=&#39;application/json&#39;<br>        )<br><br>@app.route(route=&quot;retreivemessages&quot;)<br>def retreivemessages(req: func.HttpRequest) -&gt; func.HttpResponse:<br>    linkedin_username = os.environ.get(&#39;LINKEDIN_USERNAME&#39;)<br>    linkedin_password = os.environ.get(&#39;LINKEDIN_PASSWORD&#39;)<br><br>    if not linkedin_username or not linkedin_password:<br>        return func.HttpResponse(<br>            &quot;LinkedIn credentials are not set&quot;,<br>            status_code=400<br>        )<br><br>    linkedin = Linkedin(linkedin_username, linkedin_password)<br>    <br>    conversations = linkedin.get_conversations()  # Fetch list of conversations<br><br>    return func.HttpResponse(json.dumps(conversations), mimetype=&quot;application/json&quot;)<br><br>@app.route(route=&quot;replymessage&quot;)<br>def replymessage(req: func.HttpRequest) -&gt; func.HttpResponse:<br>    linkedin_username = os.environ.get(&#39;LINKEDIN_USERNAME&#39;)<br>    linkedin_password = os.environ.get(&#39;LINKEDIN_PASSWORD&#39;)<br><br>    if not linkedin_username or not linkedin_password:<br>        return func.HttpResponse(<br>            json.dumps({&quot;message&quot;: &quot;LinkedIn credentials are not set&quot;}),<br>            status_code=400,<br>            mimetype=&quot;application/json&quot;<br>        )<br><br>    linkedin = Linkedin(linkedin_username, linkedin_password)<br><br>    try:<br>        # Parse request body<br>        req_body = req.get_json()<br>        message_body = req_body.get(&#39;message_body&#39;)<br>        conversation_urn_id = req_body.get(&#39;conversation_urn_id&#39;)<br>        recipients = req_body.get(&#39;recipients&#39;)<br><br>        if not message_body or not (conversation_urn_id or recipients):<br>            return func.HttpResponse(<br>                json.dumps({&quot;message&quot;: &quot;Invalid request&quot;}),<br>                status_code=400,<br>                mimetype=&quot;application/json&quot;<br>            )<br><br>        # Mark conversation as seen<br>        error_state_seen = linkedin.mark_conversation_as_seen(conversation_urn_id)<br>        if error_state_seen:<br>            return func.HttpResponse(<br>                json.dumps({&quot;message&quot;: &quot;Failed to mark conversation as seen&quot;}),<br>                status_code=500,<br>                mimetype=&quot;application/json&quot;<br>            )<br><br>        # Send message<br>        error_state_message = linkedin.send_message(message_body, conversation_urn_id, recipients)<br>        if error_state_message:<br>            return func.HttpResponse(<br>                json.dumps({&quot;message&quot;: &quot;Failed to send message&quot;}),<br>                status_code=500,<br>                mimetype=&quot;application/json&quot;<br>            )<br><br>        return func.HttpResponse(<br>            json.dumps({&quot;message&quot;: &quot;Message processed successfully&quot;}),<br>            status_code=200,<br>            mimetype=&quot;application/json&quot;<br>        )<br><br>    except ValueError:<br>        return func.HttpResponse(<br>            json.dumps({&quot;message&quot;: &quot;Invalid payload&quot;}),<br>            status_code=400,<br>            mimetype=&quot;application/json&quot;<br>        )<br>    except Exception as e:<br>        return func.HttpResponse(<br>            json.dumps({&quot;message&quot;: f&quot;An error occurred: {str(e)}&quot;}),<br>            status_code=500,<br>            mimetype=&quot;application/json&quot;<br>        )</pre><h4>Automating Responses with Azure OpenAI</h4><p>In professional communication, the essence of timing and tone is paramount. Azure OpenAI is akin to enlisting a highly skilled communicator, capable of crafting responses that are both contextually relevant and tonally appropriate. Understanding the Task, Choosing the Right Base Prompt, Crafting the Reply, and Review and Send — this methodological approach not only saves time but also enhances the quality and professionalism of your interactions.</p><p>The process unfolds as follows:</p><ol><li><strong>Understanding the Task:</strong> Each LinkedIn message carries its unique essence — be it a job offer, an invitation to connect, or a query. The app first allows you to check the type of the message, and decide whether it’s something to accept, reject, or warrant more information.</li><li><strong>Choosing the Right Base Prompt:</strong> Depending on the message’s intent, a base prompt is selected. These serve as our foundational templates — one for accepting offers, one for declining, another for seeking more details, and a custom one for those messages that require a personal touch or with more complexity overall.</li><li><strong>Crafting the Reply:</strong> This is where Azure OpenAI comes into its element. The base prompt plus the relevant message elements are sent to the API, which then generates a tailored reply. The language can be adapted to suit the response type — enthusiastic and professional for acceptances, polite and constructive for rejections, inquisitive and clear for inquiries. For special cases, the custom prompt allows for a more personalized engagement, much like a nuanced conversation.</li><li><strong>Review and Send: </strong>Before any message is dispatched, it allows a review process through edition or re-generation, ensuring that every word and sentiment accurately reflects your personal brand and intention.</li></ol><p>By leveraging Azure OpenAI, we’re not just automating replies; we’re elevating the art of communication, ensuring that every message you send is a step forward in your professional journey.</p><h4>Trying Your Personal Assistant</h4><p>Testing the application was akin to teaching a new assistant, starting with simple, controlled examples before letting them handle more complex scenarios. After some adjustments and debugging, the app not only replied accurately but did so with the professional polish we aimed for.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rh5CqJl-Oonvqn1X8QaIvA.png" /><figcaption>The generated response is directly sent to the recruiter’s LinkedIn messages</figcaption></figure><h4>Deploying Locally — Keeping It In-House:</h4><p>When it came to deploying our application, the decision to do so locally was driven by the need for a controlled testing environment and the nature of the unofficial API used. Local deployment allowed for a more secure and manageable setting, fitting for a project that was more an exploration of technological capabilities and a personal challenge in rapid development with AI assistance. This approach was not about preparing the application for a wide release, but rather about deeply understanding and demonstrating what’s possible with current tools and technologies in a safe, controlled manner.</p><p>The application, therefore, remains an illustrative example of what can be achieved with Angular, Azure Functions, and Python scripting when combined creatively. It stands as a testament to the speed and efficiency possible in modern development, particularly when augmented by AI assistance. While this project may not be destined for a broader audience due to the constraints and ethical considerations of using unofficial APIs, it serves as a valuable learning tool and a snapshot of rapid innovation in the field of automated communication.</p><h4>Conclusion</h4><p>By focusing on intuitive design and efficient processes, this basic application transforms the chore of responding to messages into a quick, simple task, giving you more time to focus on what truly matters in your professional life. With this setup, you’re not automating the personal touch out of your communications; you’re enhancing and accelerating it, ensuring that every reply, while quick, is still uniquely yours.</p><p>As we conclude this guide, it’s evident that AI-assisted communication is not a futuristic concept but a current reality. This exploration was more than a technical exercise; it was an insight into the potential future of professional communication. As AI evolves, so too must our strategies for engaging with it, ensuring that we harness its potential while upholding values of integrity, authenticity, and personal touch. The journey into the future of communication is just beginning, and it is up to all of us to shape it into a landscape where technology enhances, not replaces, the human connection.</p><h4>Further Reading/Resources:</h4><ul><li><a href="https://blog.angular.io/introducing-angular-v17-4d7033312e4b">Angular 17 announcement</a></li><li><a href="https://learn.microsoft.com/en-us/azure/azure-functions/functions-reference-python">Azure Functions Python developer guide</a></li><li><a href="https://learn.microsoft.com/en-us/azure/ai-services/openai/reference">Azure OpenAI Service REST API reference</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=38909d8a1b55" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>