DEV Community

Cover image for Use Line Front-end Framework (LIFF) to enrich your bot
Kenichiro Nakamura
Kenichiro Nakamura

Posted on

Use Line Front-end Framework (LIFF) to enrich your bot

Overview

In this article, I explain how to develop a web page by using Line Front-end Framework (LIFF) SDK and enrich your bot, as well as how to use CLI too for LIFF.

I re-use some code from line-liff-starter repository.
The final version is hosted at here

LIFF application

It's a just another html page with javascript with LIFF SDK, which is registered by using LIFF API to generate unique URL to be used in LINE app.

Prerequisites

※ Even though I use Microsoft Azure to host my page, you can host it anywhere.

Prepare the environment first

Create "Channel" in LINE Developer Portal

Create Messaging API channel in LINE Developer portal, and get channel and token keys.

Please copy and keep "Your user ID" too.

Create WebApps in Azure Portal

You can use Azure Portal to create a web app, but I use CLI this time for fun.

1. Install、Azure CLI, or you can also use "Cloud Shell" on Azure Portal.

2. If you use Cloud Shall, skip to step 4. Otherwise follow the steps to login.

az login

3. Follow the instruction on the screen to complete logging in.

4. Make sure you use right Azure Subscription in case you have some.

az account list
az account set -s 'subscription id'
az account show

5. Create resource group. I created "lineliffsample" resource group in East Japan.

az group create -n lineliffsample -l japaneast

6. Next, create a application service plan and a WebApps to host LIFF application.

az appservice plan create -n lineliffappplan -g lineliffsample --sku F1
az webapp create -n <unique name for WebApps> -g lineliffsample -p lineliffappplan

7. Configure the WebApps to enable git deploy.

az webapp deployment user set --user-name <username> --password <password>
az webapp deployment source config-local-git -n <your webapp name> -g lineliffsample

Use LIFF CLI with existing page

You can actually register any page as LIFF. In this section, I explan how to use CLI tool to register a page as LIFF.

1. Install liff (LIFF CLI tool).

npm install -g liff

2. Run liff init to initialize the tool.

liff init <Channel Access Token>

3. Register LIFF application. I use "tall" here but you can try "full" and "compact" as well.

liff add https://linecorp.com/en/ tall

4. Get LIFF ID and Accessible URL.

5. Now, send the address by using "liff send" command or you can even send it via LINE to your friend. It works outside of the bot, too.

liff send <LIFF ID> <User ID>

Develop LIFF application

In this section, I explain how to develop LIFF application with LIFF SDK.

Create a project

1. Create MyFirstLiff folder and open it via Visual Studio Code.

mkdir MyFirstLiff
cd MyFirstLiff
code .

2. Create index.html.

image.png

3. Copy and paste following code to index.html.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>My First LIFF</title>
</head>
<body>
    <div id="liffdata">
        <h2>LIFF Data</h2>
        <table border="1">
            <tr>
                <th>language</th>
                <td id="languagefield"></td>
            </tr>
            <tr>
                <th>context.viewType</th>
                <td id="viewtypefield"></td>
            </tr>
            <tr>
                <th>context.userId</th>
                <td id="useridfield"></td>
            </tr>
            <tr>
                <th>context.utouId</th>
                <td id="utouidfield"></td>
            </tr>
            <tr>
                <th>context.roomId</th>
                <td id="roomidfield"></td>
            </tr>
            <tr>
                <th>context.groupId</th>
                <td id="groupidfield"></td>
            </tr>
        </table>
    </div>

    <!-- Load LIFF SDK -->
    <script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script>
    <script src="liff.js"></script>
</body>
</html>

4. Add liff.js in the same directory and paste the followig code.

window.onload = function (e) {
    // initialize and get basic information
    // https://developers.line.me/en/reference/liff/#initialize-liff-app
    liff.init(function (data) {
        initializeApp(data);
    });
};

function initializeApp(data) {
    document.getElementById('languagefield').textContent = data.language;
    document.getElementById('viewtypefield').textContent = data.context.viewType;
    document.getElementById('useridfield').textContent = data.context.userId;
    document.getElementById('utouidfield').textContent = data.context.utouId;
    document.getElementById('roomidfield').textContent = data.context.roomId;
    document.getElementById('groupidfield').textContent = data.context.groupId;
}

5. Save all.

Deploy LIFF Application.

1. Initialize git. Open the "Integrated terminal" and run git init.

git init

2. Add remote which you obtain while creating WebApps above.

git remote add origin <remote git address of your WebApps> 

3. Add all and commit.

git add .
git commit -m "initial"

4. Push to deploy.

git push -u origin master

5. Use LIFF CLI to register.

liff add <WebApps address> tall

6. Now you can use the LIFF address to test. Make sure you get user ID and language of yourself.

Get user profile

1. Replace following code to index.html.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>My First LIFF</title>
</head>
<body>   
    <div id="profileinfo">
        <h2>Profile</h2>
        <div id="profilepicturediv">
        </div>
        <table border="1">
            <tr>
                <th>userId</th>
                <td id="useridprofilefield"></td>
            </tr>
            <tr>
                <th>displayName</th>
                <td id="displaynamefield"></td>
            </tr>
            <tr>
                <th>statusMessage</th>
                <td id="statusmessagefield"></td>
            </tr>
        </table>
    </div>

    <div id="liffdata">
        <h2>LIFF Data</h2>
        <table border="1">
            <tr>
                <th>language</th>
                <td id="languagefield"></td>
            </tr>
            <tr>
                <th>context.viewType</th>
                <td id="viewtypefield"></td>
            </tr>
            <tr>
                <th>context.userId</th>
                <td id="useridfield"></td>
            </tr>
            <tr>
                <th>context.utouId</th>
                <td id="utouidfield"></td>
            </tr>
            <tr>
                <th>context.roomId</th>
                <td id="roomidfield"></td>
            </tr>
            <tr>
                <th>context.groupId</th>
                <td id="groupidfield"></td>
            </tr>
        </table>
    </div>

    <!-- Load LIFF SDK -->
    <script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script>
    <script src="liff.js"></script>
</body>
</html>

2. Replace code in liff.js.

window.onload = function (e) {
    // initialize and get basic information
    // https://developers.line.me/en/reference/liff/#initialize-liff-app
    liff.init(function (data) {
        getProfile();
        initializeApp(data);
    });
};

// Get profile and display
function getProfile(){
    // https://developers.line.me/en/reference/liff/#liffgetprofile()
    liff.getProfile().then(function (profile) {
        document.getElementById('useridprofilefield').textContent = profile.userId;
        document.getElementById('displaynamefield').textContent = profile.displayName;

        var profilePictureDiv = document.getElementById('profilepicturediv');
        if (profilePictureDiv.firstElementChild) {
            profilePictureDiv.removeChild(profilePictureDiv.firstElementChild);
        }
        var img = document.createElement('img');
        img.src = profile.pictureUrl;
        img.alt = "Profile Picture";
        img.width = 200;
        profilePictureDiv.appendChild(img);

        document.getElementById('statusmessagefield').textContent = profile.statusMessage;
    }).catch(function (error) {
        window.alert("Error getting profile: " + error);
    });
}

function initializeApp(data) {
    document.getElementById('languagefield').textContent = data.language;
    document.getElementById('viewtypefield').textContent = data.context.viewType;
    document.getElementById('useridfield').textContent = data.context.userId;
    document.getElementById('utouidfield').textContent = data.context.utouId;
    document.getElementById('roomidfield').textContent = data.context.roomId;
    document.getElementById('groupidfield').textContent = data.context.groupId;
}

3. push the change.

git commit -am "add profile"
git push origin master

4. As LIFF application address didn't change at all, simply reopen the page to confirm the behavior.

Send message from LIFF

You can send messages to LINE bot from LIFF application. This is great feature to integrate the page into bot.

See detail at liff.sendMessages()

1. Replcae the code for index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>My First LIFF</title>
</head>
<body>   
    <button id="sendmessagebutton">Send Message</button>
    <div id="profileinfo">
        <h2>Profile</h2>
        <div id="profilepicturediv">
        </div>
        <table border="1">
            <tr>
                <th>userId</th>
                <td id="useridprofilefield"></td>
            </tr>
            <tr>
                <th>displayName</th>
                <td id="displaynamefield"></td>
            </tr>
            <tr>
                <th>statusMessage</th>
                <td id="statusmessagefield"></td>
            </tr>
        </table>
    </div>

    <div id="liffdata">
        <h2>LIFF Data</h2>
        <table border="1">
            <tr>
                <th>language</th>
                <td id="languagefield"></td>
            </tr>
            <tr>
                <th>context.viewType</th>
                <td id="viewtypefield"></td>
            </tr>
            <tr>
                <th>context.userId</th>
                <td id="useridfield"></td>
            </tr>
            <tr>
                <th>context.utouId</th>
                <td id="utouidfield"></td>
            </tr>
            <tr>
                <th>context.roomId</th>
                <td id="roomidfield"></td>
            </tr>
            <tr>
                <th>context.groupId</th>
                <td id="groupidfield"></td>
            </tr>
        </table>
    </div>

    <!-- Load LIFF SDK -->
    <script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script>
    <script src="liff.js"></script>
</body>
</html>

2. Replace code of liff.js

window.onload = function (e) {
    // initialize and get basic information
    // https://developers.line.me/en/reference/liff/#initialize-liff-app
    liff.init(function (data) {
        getProfile();
        initializeApp(data);
    });

    // Send message
    document.getElementById('sendmessagebutton').addEventListener('click', function () {
        // https://developers.line.me/en/reference/liff/#liffsendmessages()
        liff.sendMessages([{
            type: 'text',
            text: "Send text message"
        }, {
            type: 'sticker',
            packageId: '2',
            stickerId: '144'
        }]).then(function () {
            window.alert("Sent");
        }).catch(function (error) {
            window.alert("Error sending message: " + error);
        });
    });
};

// Get profile and display
function getProfile(){
    // https://developers.line.me/en/reference/liff/#liffgetprofile()
    liff.getProfile().then(function (profile) {
        document.getElementById('useridprofilefield').textContent = profile.userId;
        document.getElementById('displaynamefield').textContent = profile.displayName;

        var profilePictureDiv = document.getElementById('profilepicturediv');
        if (profilePictureDiv.firstElementChild) {
            profilePictureDiv.removeChild(profilePictureDiv.firstElementChild);
        }
        var img = document.createElement('img');
        img.src = profile.pictureUrl;
        img.alt = "Profile Picture";
        img.width = 200;
        profilePictureDiv.appendChild(img);

        document.getElementById('statusmessagefield').textContent = profile.statusMessage;
    }).catch(function (error) {
        window.alert("Error getting profile: " + error);
    });
}

function initializeApp(data) {
    document.getElementById('languagefield').textContent = data.language;
    document.getElementById('viewtypefield').textContent = data.context.viewType;
    document.getElementById('useridfield').textContent = data.context.userId;
    document.getElementById('utouidfield').textContent = data.context.utouId;
    document.getElementById('roomidfield').textContent = data.context.roomId;
    document.getElementById('groupidfield').textContent = data.context.groupId;
}

3. Push again.

git commit -am "add send message"
git push origin master

4. Use same LIFF address to confirm the behavior.

Open new window from LIFF

You can open URL inside LINE or outside of LINE from LIFF.

1. Replace code of index.html.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>My First LIFF</title>
</head>
<body>   
    <button id="openwindowbutton">Open the window in LINE</button>
    <button id="openwindowexternalbutton">Open the window outside of LINE</button>
    <button id="sendmessagebutton">Send Message</button>
    <div id="profileinfo">
        <h2>Profile</h2>
        <div id="profilepicturediv">
        </div>
        <table border="1">
            <tr>
                <th>userId</th>
                <td id="useridprofilefield"></td>
            </tr>
            <tr>
                <th>displayName</th>
                <td id="displaynamefield"></td>
            </tr>
            <tr>
                <th>statusMessage</th>
                <td id="statusmessagefield"></td>
            </tr>
        </table>
    </div>

    <div id="liffdata">
        <h2>LIFF Data</h2>
        <table border="1">
            <tr>
                <th>language</th>
                <td id="languagefield"></td>
            </tr>
            <tr>
                <th>context.viewType</th>
                <td id="viewtypefield"></td>
            </tr>
            <tr>
                <th>context.userId</th>
                <td id="useridfield"></td>
            </tr>
            <tr>
                <th>context.utouId</th>
                <td id="utouidfield"></td>
            </tr>
            <tr>
                <th>context.roomId</th>
                <td id="roomidfield"></td>
            </tr>
            <tr>
                <th>context.groupId</th>
                <td id="groupidfield"></td>
            </tr>
        </table>
    </div>

    <!-- Load LIFF SDK -->
    <script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script>
    <script src="liff.js"></script>
</body>
</html>

2. Replace code of liff.js

window.onload = function (e) {
    // initialize and get basic information
    // https://developers.line.me/en/reference/liff/#initialize-liff-app
    liff.init(function (data) {
        getProfile();
        initializeApp(data);
    });

    // Open window
    // https://developers.line.me/en/reference/liff/#liffopenwindow()
    document.getElementById('openwindowbutton').addEventListener('click', function () {
        liff.openWindow({
            url: 'https://line.me'
        });
    });

    document.getElementById('openwindowexternalbutton').addEventListener('click', function () {
        liff.openWindow({
            url: 'https://line.me',
            external: true
        });
    });

    // Send message
    document.getElementById('sendmessagebutton').addEventListener('click', function () {
        // https://developers.line.me/en/reference/liff/#liffsendmessages()
        liff.sendMessages([{
            type: 'text',
            text: "Send text message"
        }, {
            type: 'sticker',
            packageId: '2',
            stickerId: '144'
        }]).then(function () {
            window.alert("Sent");
        }).catch(function (error) {
            window.alert("Error sending message: " + error);
        });
    });
};

// Get profile and display
function getProfile(){
    // https://developers.line.me/en/reference/liff/#liffgetprofile()
    liff.getProfile().then(function (profile) {
        document.getElementById('useridprofilefield').textContent = profile.userId;
        document.getElementById('displaynamefield').textContent = profile.displayName;

        var profilePictureDiv = document.getElementById('profilepicturediv');
        if (profilePictureDiv.firstElementChild) {
            profilePictureDiv.removeChild(profilePictureDiv.firstElementChild);
        }
        var img = document.createElement('img');
        img.src = profile.pictureUrl;
        img.alt = "Profile Picture";
        img.width = 200;
        profilePictureDiv.appendChild(img);

        document.getElementById('statusmessagefield').textContent = profile.statusMessage;
    }).catch(function (error) {
        window.alert("Error getting profile: " + error);
    });
}

function initializeApp(data) {
    document.getElementById('languagefield').textContent = data.language;
    document.getElementById('viewtypefield').textContent = data.context.viewType;
    document.getElementById('useridfield').textContent = data.context.userId;
    document.getElementById('utouidfield').textContent = data.context.utouId;
    document.getElementById('roomidfield').textContent = data.context.roomId;
    document.getElementById('groupidfield').textContent = data.context.groupId;
}

3. Push again.

git commit -am "add openwindow"
git push origin master

4. Use same LIFF URL to confirm the behavior.

Close LIFF application

User can close the LIFF by clicking [x] button or tap outside of LIFF area, but you can also close it by using SDK.

1. Replace code of index.html.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>My First LIFF</title>
</head>
<body>           
    <button id="closewindowbutton">Close LIFF application</button>
    <button id="openwindowbutton">Open the window in LINE</button>
    <button id="openwindowexternalbutton">Open the window outside of LINE</button>
    <button id="sendmessagebutton">Send Message</button>
    <div id="profileinfo">
        <h2>Profile</h2>
        <div id="profilepicturediv">
        </div>
        <table border="1">
            <tr>
                <th>userId</th>
                <td id="useridprofilefield"></td>
            </tr>
            <tr>
                <th>displayName</th>
                <td id="displaynamefield"></td>
            </tr>
            <tr>
                <th>statusMessage</th>
                <td id="statusmessagefield"></td>
            </tr>
        </table>
    </div>

    <div id="liffdata">
        <h2>LIFF Data</h2>
        <table border="1">
            <tr>
                <th>language</th>
                <td id="languagefield"></td>
            </tr>
            <tr>
                <th>context.viewType</th>
                <td id="viewtypefield"></td>
            </tr>
            <tr>
                <th>context.userId</th>
                <td id="useridfield"></td>
            </tr>
            <tr>
                <th>context.utouId</th>
                <td id="utouidfield"></td>
            </tr>
            <tr>
                <th>context.roomId</th>
                <td id="roomidfield"></td>
            </tr>
            <tr>
                <th>context.groupId</th>
                <td id="groupidfield"></td>
            </tr>
        </table>
    </div>

    <!-- Load LIFF SDK -->
    <script src="https://d.line-scdn.net/liff/1.0/sdk.js"></script>
    <script src="liff.js"></script>
</body>
</html>

2. Replace code of liff.js.

window.onload = function (e) {
    // initialize and get basic information
    // https://developers.line.me/en/reference/liff/#initialize-liff-app
    liff.init(function (data) {
        getProfile();
        initializeApp(data);
    });

    // Close LIFF application
    // https://developers.line.me/en/reference/liff/#liffclosewindow()
    document.getElementById('closewindowbutton').addEventListener('click', function () {
        liff.closeWindow();
    });

    // Open window
    // https://developers.line.me/en/reference/liff/#liffopenwindow()
    document.getElementById('openwindowbutton').addEventListener('click', function () {
        liff.openWindow({
            url: 'https://line.me'
        });
    });

    document.getElementById('openwindowexternalbutton').addEventListener('click', function () {
        liff.openWindow({
            url: 'https://line.me',
            external: true
        });
    });

    // Send message
    document.getElementById('sendmessagebutton').addEventListener('click', function () {
        // https://developers.line.me/en/reference/liff/#liffsendmessages()
        liff.sendMessages([{
            type: 'text',
            text: "Send text message"
        }, {
            type: 'sticker',
            packageId: '2',
            stickerId: '144'
        }]).then(function () {
            window.alert("Sent");
        }).catch(function (error) {
            window.alert("Error sending message: " + error);
        });
    });
};

// Get profile and display
function getProfile(){
    // https://developers.line.me/en/reference/liff/#liffgetprofile()
    liff.getProfile().then(function (profile) {
        document.getElementById('useridprofilefield').textContent = profile.userId;
        document.getElementById('displaynamefield').textContent = profile.displayName;

        var profilePictureDiv = document.getElementById('profilepicturediv');
        if (profilePictureDiv.firstElementChild) {
            profilePictureDiv.removeChild(profilePictureDiv.firstElementChild);
        }
        var img = document.createElement('img');
        img.src = profile.pictureUrl;
        img.alt = "Profile Picture";
        img.width = 200;
        profilePictureDiv.appendChild(img);

        document.getElementById('statusmessagefield').textContent = profile.statusMessage;
    }).catch(function (error) {
        window.alert("Error getting profile: " + error);
    });
}

function initializeApp(data) {
    document.getElementById('languagefield').textContent = data.language;
    document.getElementById('viewtypefield').textContent = data.context.viewType;
    document.getElementById('useridfield').textContent = data.context.userId;
    document.getElementById('utouidfield').textContent = data.context.utouId;
    document.getElementById('roomidfield').textContent = data.context.roomId;
    document.getElementById('groupidfield').textContent = data.context.groupId;
}

3. Push.

git commit -am "add closewindow"
git push origin master

4. Confirm the behavior.

Update or Delete LIFF application

You can update registered LIFF application by using LIFF CLI or you can delete them.

Update

liff update <liffId> <url> <type:full|tall|compact>

Delete

liff delete <liffId>
liff deleteAll

Enjoy LIFF!

Oldest comments (8)

Collapse
 
kenakamu profile image
Kenichiro Nakamura

The first thing you can do it to call the function only to see if Sdk has issue or you didn’t reach to the function.

Collapse
 
orz99 profile image
Lava • Edited

Hi Kenichiro thanks for the tutorial..
すみません、ちょっと闻いてもいいですか
How to Enable file upload outside of the Album folder in LiFF ?

HTML file upload like below, the opened file browser disable all files and can't be pickup

<input name="file" type="file" multiple />

Album is the only one can be pickup and upload.
Is any way to uplaod file other than images in LIFF?

It's ok when open LIFF in external browser. Is it the limitation of LINE itself?

Dsiable File browser
the only one could be pickup

Collapse
 
kenakamu profile image
Kenichiro Nakamura

That's an interesting question, which I don't have answer. If you run the LIFF app in external browser (as v2, you can run outside LINE) what happens?

Collapse
 
orz99 profile image
Lava

Thank you for your reply:)
Every thing is fine when running LIFF app in external browser.

Thread Thread
 
kenakamu profile image
Kenichiro Nakamura

In that case I believe it's LINE client limitation for now, unfortunately.

Thread Thread
 
orz99 profile image
Lava

Unable to understand the purpose of this restriction :(

When user install 3rd party file explorer such like "File Manager+", they can pickup any file form it on LIFF app -> upload -> switch to File Manager+

This restriction will only increase the user's inconvenience.

Thread Thread
 
kenakamu profile image
Kenichiro Nakamura

Yeah totally agree. Hope they find a way to solve it. iOS and Android may behave differently?

Collapse
 
lakshmi462 profile image
Lakshmi

Hi Kenichiro.. Thanks for the tutorial..
I was not able to download a pdf file inside liff browser but the same is working fine in liff browser. I am using jspdf to generate and download this pdf. Is there any way to download inside liff browser.
Thanks